<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jackson Kasi</title>
    <description>The latest articles on Forem by Jackson Kasi (@jacksonkasi).</description>
    <link>https://forem.com/jacksonkasi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F547201%2F40f879c7-4762-49a3-a78b-dfca7b5810dd.png</url>
      <title>Forem: Jackson Kasi</title>
      <link>https://forem.com/jacksonkasi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jacksonkasi"/>
    <language>en</language>
    <item>
      <title>Part 6: The Smart Client SDK (State Synchronization &amp; Fetch Adapters)</title>
      <dc:creator>Jackson Kasi</dc:creator>
      <pubDate>Fri, 03 Apr 2026 22:59:24 +0000</pubDate>
      <link>https://forem.com/jacksonkasi/part-6-the-smart-client-sdk-state-synchronization-fetch-adapters-3elf</link>
      <guid>https://forem.com/jacksonkasi/part-6-the-smart-client-sdk-state-synchronization-fetch-adapters-3elf</guid>
      <description>&lt;h1&gt;
  
  
  Part 6: The Smart Client SDK (State Synchronization &amp;amp; Fetch Adapters)
&lt;/h1&gt;

&lt;p&gt;Welcome back. If you’ve been following the TableCraft series, you know we aren’t here to play around with fragile abstractions or “magic” boilerplates that lock you into a single vendor. We are building robust, enterprise-grade B2B systems.&lt;/p&gt;

&lt;p&gt;Today, we look at the &lt;strong&gt;Smart Client SDK&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Creator-to-Creator" Reality
&lt;/h2&gt;

&lt;p&gt;Let’s be brutally honest for a moment. Most modern frontend boilerplates give you an illusion of speed. They hand you a chaotic global state and 50 scattered &lt;code&gt;fetch()&lt;/code&gt; calls hidden inside useEffects or server actions that blur the lines of responsibility. &lt;/p&gt;

&lt;p&gt;When your app is a weekend project, that’s fine. When you’re shipping for enterprise clients, that architecture rots faster than you can patch it. You don’t need more magic; you need discipline.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Librarian / Menu Storytelling Pattern
&lt;/h2&gt;

&lt;p&gt;Think of your client architecture not as a giant bucket of data, but as a &lt;strong&gt;Librarian&lt;/strong&gt; holding a &lt;strong&gt;Menu&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Menu (Fetch Adapters)
&lt;/h3&gt;

&lt;p&gt;The frontend component is the reader. It doesn't go wandering into the stacks (the API/database) looking for data. It reads from a strict, typed Menu. &lt;/p&gt;

&lt;p&gt;We build a single isolated adapter layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The Menu&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TableCraftSDK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;librarianFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/tenant/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TenantPayload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;librarianFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/tenant`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By forcing every request through the Librarian (&lt;code&gt;librarianFetch&lt;/code&gt;), you gain a single, impenetrable choke point. This is where you handle auth token injection, 401 retries, and global error catching. No more silent failures in random components.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Librarian (State Synchronization)
&lt;/h3&gt;

&lt;p&gt;When the Menu order is placed, the Librarian handles the synchronization. &lt;/p&gt;

&lt;p&gt;Instead of optimistic UI updates that lie to the user when a database transaction inevitably fails, the Librarian maintains a clean local cache (a ledger). It only updates the view when the backend confirms the truth.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LibrarianStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Sync the truth, not the assumption.&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notifySubscribers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why We Build This Way
&lt;/h2&gt;

&lt;p&gt;This architecture isn't about saving keystrokes. It's about building a moat. When you completely decouple your state synchronization and fetch logic from your React/Vite components, you own your application. If you ever need to rip out the backend or change the frontend framework, the Librarian and the Menu remain intact.&lt;/p&gt;

&lt;p&gt;That is how you survive enterprise security reviews and scale without tearing your hair out.&lt;/p&gt;

&lt;p&gt;Stay tuned for the next part of the TableCraft series. Keep shipping clean architecture.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>webdev</category>
      <category>react</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>How I Reverse-Engineered Cursor IDE to Run on GitHub Copilot (A Proxy Architecture Deep Dive)</title>
      <dc:creator>Jackson Kasi</dc:creator>
      <pubDate>Fri, 03 Apr 2026 17:52:21 +0000</pubDate>
      <link>https://forem.com/jacksonkasi/how-i-reverse-engineered-cursor-ide-to-run-on-github-copilot-a-proxy-architecture-deep-dive-2jin</link>
      <guid>https://forem.com/jacksonkasi/how-i-reverse-engineered-cursor-ide-to-run-on-github-copilot-a-proxy-architecture-deep-dive-2jin</guid>
      <description>&lt;p&gt;I love Cursor IDE. It is arguably the most advanced AI code editor on the market today. &lt;/p&gt;

&lt;p&gt;But I hate closed ecosystems. And I absolutely refuse to pay for two AI subscriptions when one should do the job.&lt;/p&gt;

&lt;p&gt;I already have a GitHub Copilot license. I wanted Cursor’s UI (the Composer, the terminal execution, the MCP tools) to be powered by the Copilot backend I already pay for. &lt;/p&gt;

&lt;p&gt;But Cursor is a walled garden. If you try to use their "Agent" features with your own API keys, it actively blocks you. It hardcodes its internal router to force models like &lt;code&gt;claude-3.5-sonnet&lt;/code&gt; through their proprietary billing backend.&lt;/p&gt;

&lt;p&gt;So, I decided to build a local Man-in-the-Middle (MITM) proxy. I wanted to trick the smartest IDE in the world into thinking it was talking to its own servers, while secretly tunneling everything to GitHub.&lt;/p&gt;

&lt;p&gt;Here is the deep technical breakdown of how I reverse-engineered the handshake, bypassed the router, and mutated the Abstract Syntax Trees (AST) in real-time to make it happen.&lt;/p&gt;




&lt;h3&gt;
  
  
  🏗️ The Architecture of a Heist
&lt;/h3&gt;

&lt;p&gt;To pull this off, I needed two local services running in tandem:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Port 4141 (The Auth Layer):&lt;/strong&gt; An open-source connector that handles the OAuth handshake with GitHub and exposes an OpenAI-compatible endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port 4142 (The Intelligence Proxy):&lt;/strong&gt; A Bun-powered proxy router that intercepts Cursor's outbound HTTP traffic, manipulates the payloads, and forwards them to Port 4141.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The flow looks like this:&lt;br&gt;
&lt;code&gt;Cursor UI ➡️ Proxy Router (4142) ➡️ Copilot Bridge (4141) ➡️ GitHub Servers&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Building a proxy is easy. Bypassing Cursor's internal logic was the hard part. Here are the two massive roadblocks I had to solve.&lt;/p&gt;


&lt;h3&gt;
  
  
  🕳️ Hack 1: Bypassing the Router (The &lt;code&gt;cus-&lt;/code&gt; Loophole)
&lt;/h3&gt;

&lt;p&gt;Cursor is smart. If you go into Settings and define a custom model called &lt;code&gt;claude-3.5-sonnet&lt;/code&gt; pointing to &lt;code&gt;localhost:4142&lt;/code&gt;, Cursor ignores your URL. It sees the word "claude", assumes it belongs to them, and routes the network request to &lt;code&gt;api2.cursor.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You cannot overwrite their internal routing table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; I needed a loophole. Cursor only hijacks known model names. If it doesn't recognize the model, it gracefully falls back to your custom API endpoint.&lt;/p&gt;

&lt;p&gt;So, I prepended a custom tag: &lt;code&gt;cus-&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;In Cursor, I configured the model as &lt;code&gt;cus-claude-3.5-sonnet&lt;/code&gt;. Cursor looks at this, says &lt;em&gt;"I have no idea what this is, must be a local LLM,"&lt;/em&gt; and allows the payload to escape the IDE and hit my proxy on &lt;code&gt;localhost:4142&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Inside my Bun proxy, I intercept the request body and silently strip the prefix before forwarding it to GitHub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// proxy-router.ts&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cus-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The Heist: Strip the prefix so Copilot gets the real model name&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PREFIX&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PREFIX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`🔄 Rewriting model: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetModel&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetModel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Handshake bypassed. ✅&lt;/p&gt;




&lt;h3&gt;
  
  
  🧬 Hack 2: The AST Schema Mutation (The Final Boss)
&lt;/h3&gt;

&lt;p&gt;Routing the request was only 10% of the battle. Getting the Agent to actually write code was a nightmare.&lt;/p&gt;

&lt;p&gt;When you use Cursor's Composer or Agent features, the IDE sends massive arrays of Tool Calling instructions (File Editors, Linter commands, Terminal executors). &lt;/p&gt;

&lt;p&gt;Because Cursor partners heavily with Anthropic, their frontend is hardcoded to send &lt;strong&gt;Anthropic-flavored&lt;/strong&gt; tool schemas (using the &lt;code&gt;input_schema&lt;/code&gt; key). &lt;/p&gt;

&lt;p&gt;But GitHub Copilot's API expects strict &lt;strong&gt;OpenAI-flavored&lt;/strong&gt; function calls (using the &lt;code&gt;parameters&lt;/code&gt; key wrapped inside a &lt;code&gt;function&lt;/code&gt; object).&lt;/p&gt;

&lt;p&gt;If you just forward Cursor's request to Copilot, Copilot’s backend immediately throws a &lt;code&gt;400 Bad Request: Invalid Schema&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Worse, Cursor injects illegal JSON schema properties deep inside the tool definitions—things like &lt;code&gt;additionalProperties: false&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, and &lt;code&gt;$schema&lt;/code&gt;. Copilot’s strict parser rejects all of these.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Solution: Real-Time AST Cleansing
&lt;/h4&gt;

&lt;p&gt;I couldn't just use &lt;code&gt;JSON.stringify.replace()&lt;/code&gt;. The payload is too complex. &lt;/p&gt;

&lt;p&gt;I had to write a recursive AST cleaner inside the proxy. This function intercepts the HTTP stream in milliseconds, walks the entire JSON tree, and mutates it on the fly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// --- HELPER: Recursively clean illegal properties from the AST ---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cleanSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Copilot's OpenAI parser hates these. Nuke them.&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;additionalProperties&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;additionalProperties&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$schema&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$schema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Recurse into nested object properties&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;cleanSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Recurse into arrays&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;cleanSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have a weapon to clean the AST, we intercept Cursor's &lt;code&gt;tools&lt;/code&gt; array, dynamically translate the Anthropic shapes into strict OpenAI formats, and run the cleaner over every single tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// --- TRANSFORM TOOLS (Anthropic -&amp;gt; OpenAI) ---&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Cursor uses input_schema. OpenAI wants parameters.&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input_schema&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

        &lt;span class="c1"&gt;// Mutate the AST to remove illegal tokens&lt;/span&gt;
        &lt;span class="nx"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cleanSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Reconstruct the tool in strict OpenAI format&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parameters&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🤯 The Result: Absolute Freedom
&lt;/h3&gt;

&lt;p&gt;When I spun up the proxy and asked Cursor's Agent to build a React component... it worked.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cursor’s frontend thinks it’s talking to its proprietary Anthropic backend. &lt;/li&gt;
&lt;li&gt;GitHub Copilot thinks it’s serving a standard VS Code extension via OpenAI spec. &lt;/li&gt;
&lt;li&gt;I sit in the middle, seamlessly translating their protocols in real-time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Suddenly, Cursor's "Agent Mode", Terminal Execution, and MCP Tools started working perfectly... powered entirely by my existing Copilot license. Zero duplicate subscriptions. Total freedom.&lt;/p&gt;

&lt;p&gt;I open-sourced the entire proxy router so you can run it yourself.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/jacksonkasi1/copilot-for-cursor" rel="noopener noreferrer"&gt;Study the Source Code on GitHub&lt;/a&gt;&lt;/strong&gt; (&lt;code&gt;jacksonkasi1/copilot-for-cursor&lt;/code&gt;)&lt;/p&gt;




&lt;h3&gt;
  
  
  📚 Deeper Exploration
&lt;/h3&gt;

&lt;p&gt;If you are fascinated by this kind of reverse engineering and API mutation, here are the foundational concepts you should explore next:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Man-in-the-Middle (MITM) Proxies:&lt;/strong&gt; To figure out what Cursor was sending in the first place, I had to intercept the traffic. Tools like &lt;code&gt;mitmproxy&lt;/code&gt; or &lt;code&gt;Burp Suite&lt;/code&gt; are essential for inspecting encrypted HTTPS traffic and dealing with SSL Certificate Pinning. &lt;a href="https://medium.com/better-practices/reverse-engineering-an-api-403fae885303" rel="noopener noreferrer"&gt;Read more on reverse engineering private APIs with MITM proxies&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM Tool Calling Schemas:&lt;/strong&gt; The AI industry is heavily fragmented. Understanding the exact schema differences between &lt;a href="https://docs.anthropic.com/en/docs/tool-use" rel="noopener noreferrer"&gt;Anthropic's Tool Use (input_schema)&lt;/a&gt; and &lt;a href="https://platform.openai.com/docs/guides/function-calling" rel="noopener noreferrer"&gt;OpenAI's Function Calling (parameters)&lt;/a&gt; is critical if you want to build cross-platform AI tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AST (Abstract Syntax Tree) Traversal:&lt;/strong&gt; The recursive &lt;code&gt;cleanSchema&lt;/code&gt; function is a basic form of AST traversal. If you want to write compilers, linters, or advanced proxies, mastering Depth-First Search (DFS) on JSON trees is a mandatory skill.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Have you ever had to build a Man-in-the-Middle proxy to bypass a vendor lock-in? Let's talk about reverse-engineering in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>reverseengineering</category>
      <category>hacking</category>
      <category>architecture</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Stop Writing Frontend Types: Building a Backend-Driven Metadata Protocol</title>
      <dc:creator>Jackson Kasi</dc:creator>
      <pubDate>Fri, 03 Apr 2026 17:22:37 +0000</pubDate>
      <link>https://forem.com/jacksonkasi/stop-writing-frontend-types-building-a-backend-driven-metadata-protocol-1ie9</link>
      <guid>https://forem.com/jacksonkasi/stop-writing-frontend-types-building-a-backend-driven-metadata-protocol-1ie9</guid>
      <description>&lt;p&gt;&lt;em&gt;(This is Part 5 of my series on building scalable infrastructure. Catch up on &lt;a href="https://dev.to/jacksonkasi/stop-hand-wiring-react-tables-how-i-bridged-drizzle-orm-and-tanstack-in-5-minutes-11f0"&gt;Part 1: Bridging Drizzle &amp;amp; TanStack&lt;/a&gt;, &lt;a href="https://dev.to/jacksonkasi/how-to-build-framework-agnostic-open-source-tools-the-engine-adapter-pattern-1h72"&gt;Part 2: The Engine-Adapter Pattern&lt;/a&gt;, &lt;a href="https://dev.to/jacksonkasi/the-algorithm-that-killed-10000-lines-of-api-boilerplate-building-a-dynamic-query-compiler-1jba"&gt;Part 3: Dynamic Query Compilers&lt;/a&gt;, and &lt;a href="https://dev.to/jacksonkasi/building-a-cross-relational-search-engine-in-drizzle-orm-no-hardcoded-where-clauses-g50"&gt;Part 4: Cross-Relational Search&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  📖 The Chef, The Waiter, and The Menu
&lt;/h3&gt;

&lt;p&gt;Imagine running a restaurant. The Chef (your Backend) invents a brand new dish. &lt;/p&gt;

&lt;p&gt;If your restaurant operates like most tech stacks, the Chef just throws the food out the window. The Waiter (your Frontend) has to catch the food, inspect it, guess what the ingredients are, and write down a manual description for the customer. If the Chef changes the recipe tomorrow, the Waiter serves the wrong description and the customer gets angry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Smart Way:&lt;/strong&gt; The Chef prints a Menu (Metadata). When the dish changes, the Menu changes automatically. The Waiter just reads the Menu.&lt;/p&gt;




&lt;h3&gt;
  
  
  🛑 The Problem: The Type-Sync Hell
&lt;/h3&gt;

&lt;p&gt;If you are a full-stack developer, you know the pain of adding a single column to a database table.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You add &lt;code&gt;status&lt;/code&gt; to your Postgres database.&lt;/li&gt;
&lt;li&gt;You update your Drizzle ORM schema.&lt;/li&gt;
&lt;li&gt;You update your Zod validation schema.&lt;/li&gt;
&lt;li&gt;You update your backend API response type.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You switch to the frontend repository.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;You update the TypeScript interface for the API fetcher.&lt;/li&gt;
&lt;li&gt;You update the TanStack Table &lt;code&gt;columns&lt;/code&gt; definition array so the table actually renders the new column.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You are acting as a human translator between your backend and your frontend. This is exactly how engineering teams slow down and bugs slip into production.&lt;/p&gt;

&lt;p&gt;When I built &lt;a href="https://github.com/jacksonkasi1/TableCraft" rel="noopener noreferrer"&gt;TableCraft&lt;/a&gt;, I wanted to eliminate steps 3 through 7 entirely. &lt;/p&gt;

&lt;p&gt;If I define a table on the backend, the frontend should just &lt;em&gt;know&lt;/em&gt; how to render it. I needed a &lt;strong&gt;Backend-Driven Metadata Protocol&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is how you build a system where your frontend automatically writes its own code based on your backend database.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧠 The Core Concept: Schema Reflection
&lt;/h3&gt;

&lt;p&gt;In traditional REST APIs, the server only returns &lt;em&gt;data&lt;/em&gt;. &lt;br&gt;
In a Metadata Protocol, the server returns &lt;em&gt;data&lt;/em&gt; AND &lt;em&gt;the shape of the data&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When we define a table in TableCraft, we pass it the Drizzle schema object. Drizzle objects contain hidden metadata about the SQL types (e.g., is this a &lt;code&gt;varchar&lt;/code&gt;, a &lt;code&gt;boolean&lt;/code&gt;, or a &lt;code&gt;timestamp&lt;/code&gt;?).&lt;/p&gt;

&lt;p&gt;We can extract this metadata and serve it via a dedicated &lt;code&gt;/meta&lt;/code&gt; endpoint.&lt;/p&gt;
&lt;h4&gt;
  
  
  1. The Metadata Builder
&lt;/h4&gt;

&lt;p&gt;Instead of just parsing queries, our engine needs to inspect the database schema and build a JSON representation of what is allowed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getTableColumns&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drizzle-orm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildTableMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tableDefinition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTableColumns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tableDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;searchableFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tableDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowedSearchFields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sortableFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tableDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowedSortFields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// If we hid the column (like 'password'), skip it!&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tableDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hiddenColumns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 'string', 'number', 'boolean'&lt;/span&gt;
      &lt;span class="na"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notNull&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;primaryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, every TableCraft endpoint automatically exposes a &lt;code&gt;GET /api/engine/users/meta&lt;/code&gt; route. It returns a pure JSON schema of what the frontend is allowed to do.&lt;/p&gt;




&lt;h3&gt;
  
  
  🏗️ The Codegen CLI: Writing Code with Code
&lt;/h3&gt;

&lt;p&gt;Serving JSON is great, but we want strict TypeScript types on our frontend. We don't want to parse JSON at runtime; we want intellisense in VS Code.&lt;/p&gt;

&lt;p&gt;To solve this, we build a &lt;strong&gt;Codegen CLI&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The CLI fetches the &lt;code&gt;/meta&lt;/code&gt; endpoint from your local development server, reads the JSON, and writes a &lt;code&gt;.ts&lt;/code&gt; file directly into your frontend codebase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// packages/codegen/src/generator.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateFrontendTypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outputDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Fetch the metadata from the backend&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/meta`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Start generating the TypeScript string&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;tsCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`// ⚠️ AUTO-GENERATED BY TABLECRAFT. DO NOT EDIT.\n\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;tsCode&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`export interface UsersRow {\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;colName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;colMeta&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tsType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mapSqlTypeToTypeScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;colMeta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;optionalMarker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;colMeta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nullable&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;tsCode&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;colName&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;optionalMarker&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tsType&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;;\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tsCode&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`}\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Write it to the React frontend directory!&lt;/span&gt;
  &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;outputDir&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/tablecraft-types.ts`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tsCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, instead of manually typing &lt;code&gt;interface User { ... }&lt;/code&gt;, the developer just runs &lt;code&gt;bun run tablecraft generate&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The backend dictates the types. The frontend obeys.&lt;/p&gt;




&lt;h3&gt;
  
  
  🚀 Zero-Config UI (The Holy Grail)
&lt;/h3&gt;

&lt;p&gt;Because the frontend now has access to this metadata, the React client doesn't need you to manually define TanStack Table columns.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;@tablecraft/table&lt;/code&gt; React package reads the generated metadata and constructs the UI dynamically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DataTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createTableCraftAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tablecraft/table&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The adapter reads the generated metadata internally!&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;adapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTableCraftAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/engine&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// You do not pass columns. You do not pass types. &lt;/span&gt;
&lt;span class="c1"&gt;// It renders the table, the filter dropdowns, and the search box automatically.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UsersPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DataTable&lt;/span&gt; &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;adapter&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you add an &lt;code&gt;is_active&lt;/code&gt; boolean column to your Postgres database, you just restart your server. The metadata updates, the codegen CLI pulls the new types, and your React table instantly displays an &lt;code&gt;is_active&lt;/code&gt; column with a True/False filter toggle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero manual frontend work.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🛑 The Shift to Backend-Driven UI
&lt;/h3&gt;

&lt;p&gt;If you are a creator building full-stack tools, you need to think about &lt;strong&gt;Reflection&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Your backend is the source of truth. Stop making your users manually duplicate that truth on the frontend. Expose the metadata, build a codegen tool, and let the machines write the boilerplate.&lt;/p&gt;

&lt;p&gt;If you want to see exactly how the Codegen CLI generates TanStack Table definitions and Zod schemas on the fly, dig into the open-source codebase here:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/jacksonkasi1/TableCraft" rel="noopener noreferrer"&gt;Study the Metadata Codegen Protocol in TableCraft (GitHub)&lt;/a&gt;&lt;/strong&gt; (&lt;code&gt;jacksonkasi1/TableCraft&lt;/code&gt;)&lt;/p&gt;




&lt;h3&gt;
  
  
  🔄 What's Next in the Series?
&lt;/h3&gt;

&lt;p&gt;We now have a dynamic backend compiler and an auto-generated frontend. But how do they talk to each other safely? &lt;/p&gt;

&lt;p&gt;When a user clicks "Sort by Date", how does the React state seamlessly convert into the &lt;code&gt;?sort=-date&lt;/code&gt; URL parameter without creating messy &lt;code&gt;useEffect&lt;/code&gt; loops in your components?&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 6&lt;/strong&gt;, we will dive into &lt;strong&gt;The Smart Client SDK&lt;/strong&gt;—how to build framework-agnostic fetch adapters that handle state synchronization, debouncing, and secure input serialization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hit the Follow button&lt;/strong&gt; so you don't miss it. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Question for the full-stack devs: Are you currently using a Codegen tool (like GraphQL Codegen, tRPC, or OpenAPI), or are you still hand-typing your frontend interfaces? Let's discuss in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>react</category>
      <category>opensource</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Building a Cross-Relational Search Engine in Drizzle ORM (No Hardcoded WHERE Clauses)</title>
      <dc:creator>Jackson Kasi</dc:creator>
      <pubDate>Fri, 03 Apr 2026 17:16:28 +0000</pubDate>
      <link>https://forem.com/jacksonkasi/building-a-cross-relational-search-engine-in-drizzle-orm-no-hardcoded-where-clauses-g50</link>
      <guid>https://forem.com/jacksonkasi/building-a-cross-relational-search-engine-in-drizzle-orm-no-hardcoded-where-clauses-g50</guid>
      <description>&lt;p&gt;&lt;em&gt;(This is Part 4 of my series on building scalable infrastructure. If you missed them, check out &lt;a href="https://dev.to/jacksonkasi/stop-hand-wiring-react-tables-how-i-bridged-drizzle-orm-and-tanstack-in-5-minutes-11f0"&gt;Part 1: Bridging Drizzle &amp;amp; TanStack&lt;/a&gt;, &lt;a href="https://dev.to/jacksonkasi/how-to-build-framework-agnostic-open-source-tools-the-engine-adapter-pattern-1h72"&gt;Part 2: The Engine-Adapter Pattern&lt;/a&gt;, and &lt;a href="https://dev.to/jacksonkasi/the-algorithm-that-killed-10000-lines-of-api-boilerplate-building-a-dynamic-query-compiler-1jba"&gt;Part 3: The Dynamic Query Compiler&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  📖 The Librarian, The Boxes, and The Database
&lt;/h3&gt;

&lt;p&gt;Imagine you walk into a massive library. You ask the librarian: &lt;em&gt;"Can you give me a list of authors who have written a book with the word 'Magic' in the title?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approach A (The Bad Way):&lt;/strong&gt;&lt;br&gt;
The librarian walks into the back room, finds every single author who wrote a book with 'Magic', packs up &lt;em&gt;every single book&lt;/em&gt; that author has ever written, and drops 50 heavy boxes on your desk. You now have to manually dig through the boxes, throwing away duplicates, just to write down the author's name. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approach B (The Smart Way):&lt;/strong&gt;&lt;br&gt;
The librarian walks into the aisle, glances at the shelves, sees at least one 'Magic' book for an author, writes the author's name on a clean sheet of paper, and hands you a single piece of paper.&lt;/p&gt;


&lt;h3&gt;
  
  
  🛑 The Problem: We Are Dropping Boxes on Desks
&lt;/h3&gt;

&lt;p&gt;If you have ever built an admin dashboard, you know the nightmare of the &lt;strong&gt;"Global Search Box"&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The PM asks for a single input field that searches a user's name, their email, and the titles of their posts. &lt;/p&gt;

&lt;p&gt;If you are writing raw Drizzle ORM or SQL, your query usually mutates into a terrifying monster of &lt;code&gt;LEFT JOIN&lt;/code&gt;s and &lt;code&gt;OR&lt;/code&gt; clauses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ The "Approach A" Nightmare (Dropping Boxes)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;magic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;leftJoin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;ilike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`%&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nf"&gt;ilike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`%&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code is fundamentally broken. &lt;br&gt;
The &lt;code&gt;LEFT JOIN&lt;/code&gt; is "Approach A". It duplicates the user row for every single post they have written. If a user has 1,000 posts, your database sends 1,000 identical user records over the network. You then have to write complex &lt;code&gt;GROUP BY&lt;/code&gt; logic in your code just to get a clean list of users.&lt;/p&gt;

&lt;p&gt;When I built the engine for &lt;a href="https://github.com/jacksonkasi1/TableCraft" rel="noopener noreferrer"&gt;TableCraft&lt;/a&gt;, I needed to solve this permanently. I needed &lt;strong&gt;Approach B&lt;/strong&gt;. I needed a &lt;strong&gt;Cross-Relational Search Engine&lt;/strong&gt; that generates dynamic SQL vectors using &lt;code&gt;EXISTS&lt;/code&gt; instead of &lt;code&gt;LEFT JOIN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is the exact architectural pattern to build it.&lt;/p&gt;


&lt;h3&gt;
  
  
  🧠 The Core Concept: The SQL Vector Array
&lt;/h3&gt;

&lt;p&gt;Instead of writing a massive &lt;code&gt;where(or(...))&lt;/code&gt; block, we treat conditions as &lt;strong&gt;Vectors&lt;/strong&gt; (an array of SQL AST nodes). &lt;/p&gt;

&lt;p&gt;We dynamically build this array based on a configuration object, and then use the spread operator to evaluate it.&lt;/p&gt;

&lt;p&gt;In TableCraft, the developer defines the search boundaries at the schema level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Define exactly what the global search box is allowed to hit&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usersTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;posts.title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;// Nested relation!&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a request comes in (&lt;code&gt;?search=magic&lt;/code&gt;), the engine needs to compile &lt;code&gt;"posts.title"&lt;/code&gt; into a valid SQL subquery.&lt;/p&gt;




&lt;h3&gt;
  
  
  🏗️ Algorithm: The Cross-Relational Subquery Builder
&lt;/h3&gt;

&lt;p&gt;To avoid the &lt;code&gt;LEFT JOIN&lt;/code&gt; duplication problem, we do not join the tables at the root level. Instead, we use SQL &lt;code&gt;EXISTS&lt;/code&gt; subqueries (The Librarian's "glance at the shelf" approach).&lt;/p&gt;

&lt;p&gt;If the search target is &lt;code&gt;posts.title&lt;/code&gt;, the compiler generates this SQL under the hood:&lt;br&gt;
&lt;code&gt;EXISTS (SELECT 1 FROM posts WHERE posts.user_id = users.id AND posts.title ILIKE '%magic%')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the simplified algorithm to compile this dynamically in Drizzle:&lt;/p&gt;
&lt;h4&gt;
  
  
  1. The Search Dispatcher
&lt;/h4&gt;

&lt;p&gt;We iterate over the allowed search fields. If it's a local column (&lt;code&gt;name&lt;/code&gt;), we build a simple &lt;code&gt;ilike&lt;/code&gt;. If it has a dot (&lt;code&gt;posts.title&lt;/code&gt;), we hand it off to the Subquery Compiler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ilike&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;or&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SQL&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drizzle-orm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildGlobalSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AnyPgTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;searchFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; 
  &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;SQL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SQL&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchTerm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`%&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;searchFields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// It's a relational search! Hand off to the compiler.&lt;/span&gt;
      &lt;span class="nx"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;buildExistsSubquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// It's a local search.&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbColumn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getColumnMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbColumn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ilike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbColumn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. The Exists Subquery Compiler
&lt;/h4&gt;

&lt;p&gt;This is the magic. We use the Drizzle &lt;code&gt;getTableRelations&lt;/code&gt; introspection to find the foreign keys, then we generate the &lt;code&gt;EXISTS&lt;/code&gt; clause dynamically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drizzle-orm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildExistsSubquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;rootTable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AnyPgTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;relationPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;SQL&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;relationName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;relationPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Introspect schema metadata&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTableRelations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootTable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relationDef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;relations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;relationName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// Get the target table (e.g., 'posts')&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;relationDef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;referencedTableName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetColumn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getColumnMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetTable&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Get the foreign key columns mapping&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sourceFk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rootTable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;relationDef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetFk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetTable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;relationDef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;references&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;

  &lt;span class="c1"&gt;// Compile the SQL EXISTS AST dynamically!&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`EXISTS (
    SELECT 1 FROM &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetTable&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; 
    WHERE &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetFk&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sourceFk&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; 
    AND &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetColumn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ILIKE &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
  )`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🚀 The Power of the Dynamic Engine
&lt;/h3&gt;

&lt;p&gt;Because we abstracted the search logic into a compiler, look at what happens to our API endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Parse the URL&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// "magic"&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Compile the Dynamic SQL Vectors&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchAST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildGlobalSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;posts.title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;searchQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Execute&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchAST&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Zero hardcoded joins. Zero duplicated rows.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;If the product manager says, "Hey, can we also search by the user's company name?", you don't touch the backend API. You don't write another &lt;code&gt;LEFT JOIN&lt;/code&gt;. You just update the definition array:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.search("name", "email", "posts.title", "company.name")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The engine resolves the relation, builds the subquery, and executes perfectly.&lt;/p&gt;




&lt;h3&gt;
  
  
  🛑 Stop Hardcoding Your Backends
&lt;/h3&gt;

&lt;p&gt;If you are building SaaS platforms, internal tools, or SDKs, this is the architecture you need to adopt. &lt;/p&gt;

&lt;p&gt;Writing one-off SQL queries for every dashboard table is a massive waste of human capital. Build the engine once, and let it compile your logic for you.&lt;/p&gt;

&lt;p&gt;If you want to see how this &lt;code&gt;EXISTS&lt;/code&gt; subquery algorithm is recursively implemented for infinitely deep relations (e.g., &lt;code&gt;posts.comments.author.name&lt;/code&gt;), I open-sourced the entire engine core:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/jacksonkasi1/TableCraft" rel="noopener noreferrer"&gt;Study the Search Compiler Architecture in TableCraft (GitHub)&lt;/a&gt;&lt;/strong&gt; (&lt;code&gt;jacksonkasi1/TableCraft&lt;/code&gt;)&lt;/p&gt;




&lt;h3&gt;
  
  
  🔄 What's Next in the Series?
&lt;/h3&gt;

&lt;p&gt;We have successfully built the backend engine: It parses URLs, compiles dynamic relational filters, handles cursor pagination, and executes cross-relational global searches. &lt;/p&gt;

&lt;p&gt;But a backend API is useless without a frontend. &lt;/p&gt;

&lt;p&gt;How do you make the React frontend (TanStack Table) &lt;strong&gt;automatically know&lt;/strong&gt; what columns exist, what filters are allowed, and how to render the UI without writing duplicate TypeScript interfaces?&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 5&lt;/strong&gt;, we will dive into &lt;strong&gt;The Metadata Builder Protocol&lt;/strong&gt;—how to generate a semantic JSON schema from the Drizzle backend so your frontend builds itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hit the Follow button&lt;/strong&gt; to catch the next masterclass. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Question for the builders: Do you prefer &lt;code&gt;EXISTS&lt;/code&gt; subqueries or &lt;code&gt;LEFT JOIN&lt;/code&gt; + &lt;code&gt;GROUP BY&lt;/code&gt; when handling 1-to-many searches? Let's argue about query planners in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>database</category>
      <category>architecture</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The Algorithm That Killed 10,000 Lines of API Boilerplate (Building a Dynamic Query Compiler)</title>
      <dc:creator>Jackson Kasi</dc:creator>
      <pubDate>Fri, 03 Apr 2026 17:14:39 +0000</pubDate>
      <link>https://forem.com/jacksonkasi/the-algorithm-that-killed-10000-lines-of-api-boilerplate-building-a-dynamic-query-compiler-1jba</link>
      <guid>https://forem.com/jacksonkasi/the-algorithm-that-killed-10000-lines-of-api-boilerplate-building-a-dynamic-query-compiler-1jba</guid>
      <description>&lt;p&gt;&lt;em&gt;(This is Part 3 of my series on building scalable infrastructure. If you missed them, check out &lt;a href="https://dev.to/jacksonkasi/stop-hand-wiring-react-tables-how-i-bridged-drizzle-orm-and-tanstack-in-5-minutes-11f0"&gt;Part 1: Bridging Drizzle &amp;amp; TanStack&lt;/a&gt; and &lt;a href="https://dev.to/jacksonkasi/how-to-build-framework-agnostic-open-source-tools-the-engine-adapter-pattern-1h72"&gt;Part 2: The Engine-Adapter Pattern&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Most backend engineers spend their entire careers writing "Switchboard APIs". You know the type: an endpoint that receives &lt;code&gt;?include=posts&lt;/code&gt;, checks an &lt;code&gt;if&lt;/code&gt; statement, and manually adds a SQL &lt;code&gt;JOIN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is tedious, it is brittle, and frankly, it is boring.&lt;/p&gt;

&lt;p&gt;If you are a tool creator, you shouldn't be writing switchboards. You should be writing &lt;strong&gt;compilers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When I built the engine for &lt;a href="https://github.com/jacksonkasi1/TableCraft" rel="noopener noreferrer"&gt;TableCraft&lt;/a&gt;, I didn't want to write endpoints. I wanted to build an HTTP-to-SQL compiler that could dynamically resolve infinitely nested database relations and construct complex B-Tree optimized cursor paginations on the fly.&lt;/p&gt;

&lt;p&gt;Here are the exact algorithms and architectural patterns I used to do it. If you build infrastructure, SDKs, or internal developer platforms, read this carefully.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧠 Algorithm 1: Recursive Relation Graph Traversal (The AST Compiler)
&lt;/h3&gt;

&lt;p&gt;The hardest problem in dynamic APIs is nested relational filtering. &lt;/p&gt;

&lt;p&gt;Imagine a URL query like this: &lt;br&gt;
&lt;code&gt;?filter[posts.author.company.name][eq]=Acme&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To execute this safely in Drizzle ORM, you have to traverse from the &lt;code&gt;Users&lt;/code&gt; table, to &lt;code&gt;Posts&lt;/code&gt;, to &lt;code&gt;Authors&lt;/code&gt;, to &lt;code&gt;Companies&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;You cannot hardcode this. You need a &lt;strong&gt;Depth-First Search (DFS)&lt;/strong&gt; algorithm that walks the ORM's relational graph.&lt;/p&gt;

&lt;p&gt;Here is the architectural pattern to solve this:&lt;/p&gt;
&lt;h4&gt;
  
  
  1. The Tokenizer
&lt;/h4&gt;

&lt;p&gt;First, we tokenize the string path &lt;code&gt;posts.author.company.name&lt;/code&gt; into an array: &lt;code&gt;["posts", "author", "company", "name"]&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. The Recursive Resolver
&lt;/h4&gt;

&lt;p&gt;Next, we write a recursive compiler. It takes the token array and checks the Drizzle Schema metadata at every node to ensure the path actually exists.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getTableRelations&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drizzle-orm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;resolveRelationPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;currentTable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AnyPgTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;pathTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; 
  &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;targetTable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AnyPgTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// Base Case: We reached the final token (the column name)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pathTokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;targetTable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pathTokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Recursive Step: Extract the next relation&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;relationName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;remainingTokens&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pathTokens&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Introspect Drizzle's hidden relation metadata&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tableRelations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTableRelations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentTable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relationDef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tableRelations&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="nx"&gt;relationName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;relationDef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Security Exception: Invalid relation path '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;relationName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Find the next table in the schema dictionary&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;relationDef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;referencedTableName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// Recurse deeper into the graph&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;resolveRelationPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remainingTokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The "Wow" Factor:&lt;/strong&gt; This algorithm makes your API infinitely scalable. Whether the user joins 1 table or 10 tables, the compiler resolves the abstract syntax tree safely. If a hacker tries &lt;code&gt;?filter[posts.passwords.hash]&lt;/code&gt;, the graph traversal halts because the relation definition isn't exposed in the safe schema.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔥 Algorithm 2: Dynamic Keyset Pagination (The Math)
&lt;/h3&gt;

&lt;p&gt;Offset pagination (&lt;code&gt;OFFSET 10000 LIMIT 50&lt;/code&gt;) is a database killer. It requires Postgres to scan and discard 10,000 rows.&lt;/p&gt;

&lt;p&gt;The enterprise solution is &lt;strong&gt;Keyset Pagination&lt;/strong&gt; (Cursor Pagination). But generating dynamic keyset SQL for multi-column sorting is a mathematical nightmare.&lt;/p&gt;

&lt;p&gt;If a user sorts by &lt;code&gt;[-createdAt, id]&lt;/code&gt;, the SQL requires tuple comparison:&lt;br&gt;
&lt;code&gt;WHERE (created_at, id) &amp;lt; ('2023-10-01', 504)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;How do you generate this dynamically from a URL string?&lt;/p&gt;
&lt;h4&gt;
  
  
  The Vector Comparison Algorithm
&lt;/h4&gt;

&lt;p&gt;In TableCraft, the engine parses the &lt;code&gt;sort&lt;/code&gt; array and the &lt;code&gt;cursor&lt;/code&gt; object, then builds a vector array for Drizzle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drizzle-orm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildDynamicCursor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;sortFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AnyColumn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;asc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;desc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}[],&lt;/span&gt;
  &lt;span class="nx"&gt;cursorData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Extract the column objects and cursor values&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sortFields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sortFields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cursorData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Generate the Tuple SQL dynamically&lt;/span&gt;
  &lt;span class="c1"&gt;// If direction is 'desc', we use '&amp;lt;', if 'asc', we use '&amp;gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;operator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sortFields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;desc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Inject raw SQL into the Drizzle AST&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`(
    &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`, `&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
  ) &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; (
    &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`, `&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
  )`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By abstracting this into the engine, the frontend engineer just passes a base64 encoded cursor string. The backend parses it, feeds it to the Vector Comparison Algorithm, and generates a perfectly optimized B-Tree index scan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero sequential scans. Zero manual SQL.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🛑 The Paradigm Shift for Creators
&lt;/h3&gt;

&lt;p&gt;If you are building an open-source tool, you have to stop thinking about &lt;em&gt;endpoints&lt;/em&gt; and start thinking about &lt;em&gt;state machines&lt;/em&gt; and &lt;em&gt;compilers&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When you build a recursive AST compiler like the one inside TableCraft, you aren't just writing a library. You are giving developers a primitive engine they can wrap in any framework they want (Hono, Express, Next.js).&lt;/p&gt;

&lt;p&gt;This is what separates basic wrappers from god-tier infrastructure.&lt;/p&gt;

&lt;p&gt;I open-sourced the entire engine. If you want to see how the recursive relation builder and dynamic pagination vector math is actually wired up to the Drizzle ORM core, dig into the repository:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/jacksonkasi1/TableCraft" rel="noopener noreferrer"&gt;Deep Dive the Compiler Architecture in TableCraft (GitHub)&lt;/a&gt;&lt;/strong&gt; (&lt;code&gt;jacksonkasi1/TableCraft&lt;/code&gt;)&lt;/p&gt;




&lt;h3&gt;
  
  
  🔄 What's Next in the Series?
&lt;/h3&gt;

&lt;p&gt;We’ve covered the Engine-Adapter pattern and the Compiler Algorithms. But what happens when the user types a global search query and expects instant results across 5 deeply nested, joined tables? &lt;/p&gt;

&lt;p&gt;In the next article, I’m going to break down &lt;strong&gt;The Cross-Relational Search Engine&lt;/strong&gt;—how to dynamically build generic full-text search SQL vectors without hardcoding a single &lt;code&gt;WHERE&lt;/code&gt; clause. &lt;/p&gt;

&lt;p&gt;If you are serious about backend architecture, &lt;strong&gt;hit the Follow button&lt;/strong&gt; so you don't miss it. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;To the other infrastructure creators out there: What is the most complex algorithmic challenge you've had to solve in your core engine? Drop it in the comments below, and let's debate the best approaches.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>architecture</category>
      <category>opensource</category>
      <category>typescript</category>
    </item>
    <item>
      <title>How to Build Framework-Agnostic Open Source Tools (The Engine-Adapter Pattern)</title>
      <dc:creator>Jackson Kasi</dc:creator>
      <pubDate>Fri, 03 Apr 2026 17:13:55 +0000</pubDate>
      <link>https://forem.com/jacksonkasi/how-to-build-framework-agnostic-open-source-tools-the-engine-adapter-pattern-1h72</link>
      <guid>https://forem.com/jacksonkasi/how-to-build-framework-agnostic-open-source-tools-the-engine-adapter-pattern-1h72</guid>
      <description>&lt;p&gt;Look at your codebase right now. &lt;/p&gt;

&lt;p&gt;If you are building an open-source library, an SDK, or a generic backend tool, and you are importing &lt;code&gt;express&lt;/code&gt;, &lt;code&gt;next/server&lt;/code&gt;, or &lt;code&gt;hono&lt;/code&gt; directly into your core business logic... &lt;strong&gt;you are building a trap.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I know, because I fell into it. When you tie your logic to a specific HTTP framework, you alienate 80% of the developer ecosystem. A Next.js dev can't use your Express tool. A Hono dev can't use your Fastify plugin.&lt;/p&gt;

&lt;p&gt;If you want to build tools that &lt;em&gt;other developers actually adopt&lt;/em&gt;, you have to think like an architect, not just a coder. &lt;/p&gt;

&lt;p&gt;You need to master &lt;strong&gt;The Engine-Adapter Pattern&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;This is exactly how I built &lt;a href="https://github.com/jacksonkasi1/TableCraft" rel="noopener noreferrer"&gt;TableCraft&lt;/a&gt; to seamlessly support Hono, Express, Next.js, and Elysia from a single codebase. Let's break down how you can build this pattern for your next open-source project.&lt;/p&gt;




&lt;h3&gt;
  
  
  🛑 The Anti-Pattern: Framework Coupling
&lt;/h3&gt;

&lt;p&gt;Most developers start building an API library like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ BAD: Tightly coupled to Express&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myAwesomeLibrary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// ... core logic ...&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The moment you do this, your library is dead to anyone not using Express. &lt;/p&gt;

&lt;h3&gt;
  
  
  🏗️ The Solution: The Engine-Adapter Architecture
&lt;/h3&gt;

&lt;p&gt;To fix this, you must aggressively separate your &lt;strong&gt;Core Engine&lt;/strong&gt; from the &lt;strong&gt;HTTP Layer&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Your architecture should look like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Engine:&lt;/strong&gt; Pure TypeScript. Zero dependencies on any HTTP framework. It takes standard inputs (strings, objects) and returns standard outputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Adapters:&lt;/strong&gt; Tiny, 20-line wrappers that translate a specific framework's request into the standard input your Engine expects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's look at how to actually code this.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Build the Framework-Agnostic Engine
&lt;/h4&gt;

&lt;p&gt;Your engine should only care about raw data. It doesn't know what a "Request" or "Response" is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// packages/engine/src/index.ts&lt;/span&gt;
&lt;span class="c1"&gt;// ✅ GOOD: Pure TypeScript. No HTTP context.&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;EngineContext&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;body&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CoreEngine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EngineContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Parse the raw URL&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Execute pure business logic&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;myComplexLogic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Return a standard response object&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 2: Build the Adapters
&lt;/h4&gt;

&lt;p&gt;Now, we create isolated packages for each framework. These are the "Adapters". Their &lt;em&gt;only&lt;/em&gt; job is translation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Hono Adapter:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// packages/adapter-hono/src/index.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hono&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CoreEngine&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@my-org/engine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createHonoAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CoreEngine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Translate Hono Request -&amp;gt; Engine Context&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Translate Engine Response -&amp;gt; Hono Response&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Express Adapter:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// packages/adapter-express/src/index.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CoreEngine&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@my-org/engine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createExpressAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CoreEngine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Translate Express Request -&amp;gt; Engine Context&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Translate Engine Response -&amp;gt; Express Response&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🧠 Why This Changes Everything
&lt;/h3&gt;

&lt;p&gt;When you build like this, magic happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Infinite Reach:&lt;/strong&gt; Adding support for a new framework (like SvelteKit or Nuxt) takes 10 minutes. You just write a new 20-line adapter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;God-Tier Testing:&lt;/strong&gt; You can unit test your &lt;code&gt;CoreEngine&lt;/code&gt; perfectly without ever spinning up a mock HTTP server. You just pass it JSON objects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monorepo Ready:&lt;/strong&gt; You can use Turborepo or npm workspaces to publish &lt;code&gt;@my-org/engine&lt;/code&gt;, &lt;code&gt;@my-org/adapter-hono&lt;/code&gt;, and &lt;code&gt;@my-org/adapter-next&lt;/code&gt; as separate packages. Users only install what they need.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  🚀 See It In The Wild
&lt;/h3&gt;

&lt;p&gt;This isn't theory. This is exactly the architecture I used to build &lt;strong&gt;TableCraft&lt;/strong&gt; — a headless data table engine for Drizzle ORM.&lt;/p&gt;

&lt;p&gt;I wanted TableCraft to work for &lt;em&gt;everyone&lt;/em&gt;. So I built &lt;code&gt;@tablecraft/engine&lt;/code&gt; as a pure logic core, and exposed it via &lt;code&gt;@tablecraft/adapter-hono&lt;/code&gt;, &lt;code&gt;@tablecraft/adapter-express&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;If you are a creator, a library author, or just someone who wants to understand how scalable open-source repositories are structured, I highly recommend studying this pattern in a real codebase.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/jacksonkasi1/TableCraft" rel="noopener noreferrer"&gt;Study the Monorepo Architecture in TableCraft on GitHub&lt;/a&gt;&lt;/strong&gt; (&lt;code&gt;jacksonkasi1/TableCraft&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you are building an open-source tool right now, what is the hardest architectural decision you are currently facing? Let's talk in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>typescript</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Dynamically Map URL Queries to Type-Safe SQL (Drizzle ORM Architecture)</title>
      <dc:creator>Jackson Kasi</dc:creator>
      <pubDate>Fri, 03 Apr 2026 17:13:13 +0000</pubDate>
      <link>https://forem.com/jacksonkasi/how-to-dynamically-map-url-queries-to-type-safe-sql-drizzle-orm-architecture-cl9</link>
      <guid>https://forem.com/jacksonkasi/how-to-dynamically-map-url-queries-to-type-safe-sql-drizzle-orm-architecture-cl9</guid>
      <description>&lt;p&gt;&lt;em&gt;(This is part of my series on building scalable infrastructure. If you missed it, check out &lt;a href="https://dev.to/jacksonkasi/stop-hand-wiring-react-tables-how-i-bridged-drizzle-orm-and-tanstack-in-5-minutes-11f0"&gt;Part 1: Bridging Drizzle &amp;amp; TanStack&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;If you use an ORM like Drizzle or Prisma, you eventually run into a wall: &lt;strong&gt;How do you safely convert dynamic URL query strings into type-safe SQL queries?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine a user hits this endpoint from a data table on your frontend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/users?filter[name][ilike]=%jack%&amp;amp;filter[age][gte]=18&amp;amp;sort=-createdAt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need to convert that string into this Drizzle ORM execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;ilike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%jack%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
      &lt;span class="nf"&gt;gte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most developers write a giant, brittle &lt;code&gt;switch&lt;/code&gt; statement for every single API endpoint. It's unscalable, error-prone, and a massive security risk if not sanitized properly.&lt;/p&gt;

&lt;p&gt;In this post, we are going to look at the &lt;strong&gt;Dynamic Query Builder Architecture&lt;/strong&gt;. This is the exact pattern I used to build the engine behind &lt;a href="https://github.com/jacksonkasi1/TableCraft" rel="noopener noreferrer"&gt;TableCraft&lt;/a&gt;, and you can use it to build your own generic API endpoints.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧠 The Core Problem: String to AST
&lt;/h3&gt;

&lt;p&gt;You cannot just pass user input into database functions. You need an intermediate layer. The architecture looks like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;URL Query -&amp;gt; Parser -&amp;gt; Abstract Syntax Tree (AST) -&amp;gt; ORM Builder -&amp;gt; SQL&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let's break down how to actually build this in TypeScript with Drizzle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: The Operator Dictionary
&lt;/h3&gt;

&lt;p&gt;The first step is mapping string-based operators from the URL (e.g., &lt;code&gt;ilike&lt;/code&gt;, &lt;code&gt;gte&lt;/code&gt;, &lt;code&gt;eq&lt;/code&gt;) to actual Drizzle functions safely. &lt;/p&gt;

&lt;p&gt;Instead of &lt;code&gt;if/else&lt;/code&gt; hell, we use an &lt;strong&gt;Operator Dictionary&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ne&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;like&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ilike&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inArray&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drizzle-orm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// A secure map of allowed operations&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;operatorMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;ne&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;gte&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;gte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;lte&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;lte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;like&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;like&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;ilike&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ilike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;inArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AllowedOperator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;operatorMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a user passes &lt;code&gt;?filter[name][DROP TABLE]=...&lt;/code&gt;, the engine immediately rejects it because &lt;code&gt;DROP TABLE&lt;/code&gt; is not a valid key in &lt;code&gt;operatorMap&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Drizzle Schema Introspection
&lt;/h3&gt;

&lt;p&gt;How do we know if &lt;code&gt;users.age&lt;/code&gt; is actually a column in the database? We have to extract the metadata from the Drizzle table object dynamically.&lt;/p&gt;

&lt;p&gt;Drizzle tables hold their column definitions internally. We can build a utility to extract them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getTableColumns&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drizzle-orm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AnyPgTable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drizzle-orm/pg-core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getColumnMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AnyPgTable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTableColumns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// We map the string name to the actual Drizzle column object&lt;/span&gt;
    &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when the URL asks to filter by &lt;code&gt;age&lt;/code&gt;, we check &lt;code&gt;getColumnMap(users).has("age")&lt;/code&gt;. If it doesn't exist, we throw a 400 Bad Request. Zero SQL injection risk.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: The Filter Builder (The Magic)
&lt;/h3&gt;

&lt;p&gt;Now we write the parser. It takes the raw JSON/Query object, validates it against the schema, and builds the Drizzle &lt;code&gt;where&lt;/code&gt; array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SQL&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drizzle-orm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawFilters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AnyPgTable&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;SQL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columnMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getColumnMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SQL&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operations&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawFilters&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbColumn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;columnMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Security check: Does the column exist?&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;dbColumn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;operations&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbOp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;operatorMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;AllowedOperator&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

      &lt;span class="c1"&gt;// Security check: Is the operator allowed?&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;dbOp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// Construct the Drizzle AST&lt;/span&gt;
      &lt;span class="nx"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dbOp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbColumn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;and&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Connecting it to the ORM
&lt;/h3&gt;

&lt;p&gt;With our &lt;code&gt;buildFilters&lt;/code&gt; function, our API endpoint becomes incredibly clean. It doesn't matter if there is 1 filter or 100 filters, the code remains the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example inside an Express or Hono handler&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseUrlToFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Extracts { name: { ilike: '%jack%' } }&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;conditions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Safely inject the dynamic AST&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🚀 Why This Matters
&lt;/h3&gt;

&lt;p&gt;When you understand this pattern, you stop writing repetitive backend code. You stop writing custom endpoints for every table.&lt;/p&gt;

&lt;p&gt;Instead, you write the &lt;strong&gt;Engine&lt;/strong&gt; once, and it automatically handles filtering, sorting, and pagination for every table in your database.&lt;/p&gt;

&lt;p&gt;This exact architecture is how I built &lt;strong&gt;TableCraft&lt;/strong&gt;. It abstracts this entire engine so you can generate these dynamic, secure endpoints in one line of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usersTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-createdAt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to see how deep this rabbit hole goes (handling relational joins recursively, cursor pagination, and input validation), dive into the open-source source code here:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/jacksonkasi1/TableCraft" rel="noopener noreferrer"&gt;Explore the Architecture in TableCraft on GitHub&lt;/a&gt;&lt;/strong&gt; (&lt;code&gt;jacksonkasi1/TableCraft&lt;/code&gt;)&lt;/p&gt;




&lt;h3&gt;
  
  
  🔄 What's Next in the Series?
&lt;/h3&gt;

&lt;p&gt;Now you know how to map URL queries to safe SQL ASTs. But what if you want to expose this logic across multiple frameworks like Express, Hono, and Next.js without rewriting the core engine?&lt;/p&gt;

&lt;p&gt;In the next article, &lt;strong&gt;&lt;a href="https://dev.to/jacksonkasi/how-to-build-framework-agnostic-open-source-tools-the-engine-adapter-pattern-1h72"&gt;How to Build Framework-Agnostic Open Source Tools&lt;/a&gt;&lt;/strong&gt;, I break down the Engine-Adapter pattern that makes TableCraft universally compatible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hit the Follow button&lt;/strong&gt; so you don't miss the rest of the masterclass.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>database</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stop Hand-Wiring React Tables: How I Bridged Drizzle ORM and TanStack in 5 Minutes</title>
      <dc:creator>Jackson Kasi</dc:creator>
      <pubDate>Fri, 03 Apr 2026 17:08:54 +0000</pubDate>
      <link>https://forem.com/jacksonkasi/stop-hand-wiring-react-tables-how-i-bridged-drizzle-orm-and-tanstack-in-5-minutes-11f0</link>
      <guid>https://forem.com/jacksonkasi/stop-hand-wiring-react-tables-how-i-bridged-drizzle-orm-and-tanstack-in-5-minutes-11f0</guid>
      <description>&lt;p&gt;Most startups don't die because of a bad idea. They die because of bad engineering velocity.&lt;/p&gt;

&lt;p&gt;If your engineering team is spending days wiring up basic data tables, pagination, and sorting for your internal dashboards, you are bleeding money. &lt;/p&gt;

&lt;p&gt;I know, because I audited my own workflow. Every time a PM asked for a new view in our B2B SaaS, it meant:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setting up a new TanStack Table instance.&lt;/li&gt;
&lt;li&gt;Wiring up frontend state for &lt;code&gt;pageIndex&lt;/code&gt;, &lt;code&gt;sortBy&lt;/code&gt;, and &lt;code&gt;filters&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Writing a backend endpoint to parse &lt;code&gt;?sort=-createdAt&amp;amp;status=active&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Writing the Drizzle ORM logic to map those URL strings to actual SQL queries.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It was a brittle, soul-crushing translation layer. And it had to be rewritten for &lt;em&gt;every single table&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I decided to kill the translation layer entirely. I built &lt;strong&gt;&lt;a href="https://github.com/jacksonkasi1/TableCraft" rel="noopener noreferrer"&gt;TableCraft&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is the deep dive into the architecture, how it handles complex edge cases, and why building "systems" beats building "components."&lt;/p&gt;




&lt;h3&gt;
  
  
  🛑 The Problem: The Translation Layer Hell
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TanStack Table&lt;/strong&gt; is the gold standard for headless UI. &lt;strong&gt;Drizzle ORM&lt;/strong&gt; is the gold standard for type-safe SQL. &lt;/p&gt;

&lt;p&gt;But they don't speak the same language. &lt;/p&gt;

&lt;p&gt;When a user filters a column on the client, TanStack spits out an array of filter objects. You have to send that to your backend, validate it, and write a manual Drizzle &lt;code&gt;where: and(eq(...), like(...))&lt;/code&gt; clause. &lt;/p&gt;

&lt;p&gt;If you change a column name in your database, your frontend table breaks. If you add a new relation, you have to rewrite the API. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We were spending an hour wiring up a single table. That is unacceptable for a team trying to find Product-Market Fit."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🏗️ The Architecture: How TableCraft Works
&lt;/h3&gt;

&lt;p&gt;I structured TableCraft as a &lt;strong&gt;Turborepo&lt;/strong&gt; monorepo to separate the core engine from the framework-specific adapters. &lt;/p&gt;

&lt;p&gt;The goal was simple: &lt;strong&gt;Define the rules on the backend, and let the frontend automatically inherit them.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  1. The Engine (&lt;code&gt;@tablecraft/engine&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;Instead of writing manual API routes, you define the table behavior at the schema level.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineTable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tablecraft/engine&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./db/schema&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// We define exactly what is allowed. Zero boilerplate.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usersConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Never leaks to the client&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-createdAt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. The Framework Adapters
&lt;/h4&gt;

&lt;p&gt;I built adapters for the most aggressive modern backend frameworks: &lt;strong&gt;Hono&lt;/strong&gt;, &lt;strong&gt;Elysia&lt;/strong&gt;, &lt;strong&gt;Express&lt;/strong&gt;, and &lt;strong&gt;Next.js&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The adapter takes your &lt;code&gt;defineTable&lt;/code&gt; configs and dynamically generates the REST endpoints. It handles input validation, parses the URL query params, and safely executes the Drizzle query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createHonoApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tablecraft/adapter-hono&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// This single line generates all the filtering, sorting, and pagination APIs.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createHonoApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;configs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;usersConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. The React Client (&lt;code&gt;@tablecraft/table&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;On the frontend, you don't write &lt;code&gt;useReactTable&lt;/code&gt; from scratch anymore. The client SDK reads the metadata from the auto-generated API and constructs the table dynamically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DataTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createTableCraftAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tablecraft/table&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;adapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTableCraftAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/engine&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Drop this in. You're done.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UsersPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DataTable&lt;/span&gt; &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;adapter&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🧠 Handling the Complex Stuff
&lt;/h3&gt;

&lt;p&gt;Building a basic table generator is easy. Building one that survives an enterprise production environment is hard. Here is how TableCraft handles the complexity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Cursor Pagination:&lt;/strong&gt; Offset pagination gets exponentially slower on large datasets. The engine includes an internal &lt;code&gt;core/cursorPagination&lt;/code&gt; builder that automatically switches to cursor-based queries for massive tables.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Deep Relational Filtering:&lt;/strong&gt; Users don't just filter flat tables. They want to filter &lt;code&gt;Orders&lt;/code&gt; by the &lt;code&gt;User.email&lt;/code&gt;. The &lt;code&gt;core/relationBuilder&lt;/code&gt; recursively maps nested Drizzle relations so the frontend can query across joins effortlessly.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Soft Deletes &amp;amp; Aggregations:&lt;/strong&gt; Need to hide soft-deleted rows or show SUM/AVG rows at the bottom? Handled out of the box via &lt;code&gt;core/softDelete&lt;/code&gt; and &lt;code&gt;core/aggregationBuilder&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🏁 How It Ended: 1 Hour ➡️ 5 Minutes
&lt;/h3&gt;

&lt;p&gt;By bridging the gap between Drizzle and TanStack natively, the time to deploy a fully functional, highly complex data table went from ~1 hour of manual wiring to &lt;strong&gt;exactly 5 minutes&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The frontend engineers no longer bother the backend engineers for new filter parameters. It is all inherited automatically.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔮 What's Next?
&lt;/h3&gt;

&lt;p&gt;The engine currently supports Hono, Next.js, and Express. The next phase is expanding the ecosystem:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SvelteKit &amp;amp; Vue Adapters:&lt;/strong&gt; Because React shouldn't have all the fun.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Schema Optimization:&lt;/strong&gt; Integrating an agentic layer that reads the slow queries hitting the TableCraft engine and automatically suggests PostgreSQL indexes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are tired of writing the same pagination logic every week, stop building components. Start building systems.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/jacksonkasi1/TableCraft" rel="noopener noreferrer"&gt;Fork TableCraft on GitHub&lt;/a&gt;&lt;/strong&gt; (&lt;code&gt;jacksonkasi1/TableCraft&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Let me know in the comments how your team currently handles the Drizzle-to-Frontend translation layer!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>architecture</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>How ORM Migrations Are Hiding Missing Indexes (And How to Actually See Your Database)</title>
      <dc:creator>Jackson Kasi</dc:creator>
      <pubDate>Fri, 03 Apr 2026 17:03:04 +0000</pubDate>
      <link>https://forem.com/jacksonkasi/how-orm-migrations-are-hiding-missing-indexes-and-how-to-actually-see-your-database-27m4</link>
      <guid>https://forem.com/jacksonkasi/how-orm-migrations-are-hiding-missing-indexes-and-how-to-actually-see-your-database-27m4</guid>
      <description>&lt;p&gt;I spent 4 hours last week debugging a slow API endpoint. &lt;/p&gt;

&lt;p&gt;The culprit? A missing index on a foreign key that my ORM "helpfully" abstracted away during a schema migration.&lt;/p&gt;

&lt;p&gt;When you use modern tools like Prisma or Drizzle, it’s incredibly easy to forget what your database actually looks like. The visual connection between tables is lost in hundreds of lines of TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛑 The Hidden Danger of ORMs
&lt;/h3&gt;

&lt;p&gt;Let's look at a standard relational setup: you have &lt;code&gt;Users&lt;/code&gt; and &lt;code&gt;Orders&lt;/code&gt;. In your ORM, you define a relation. The ORM generates a migration adding a &lt;code&gt;user_id&lt;/code&gt; column to &lt;code&gt;Orders&lt;/code&gt; with a foreign key constraint.&lt;/p&gt;

&lt;p&gt;What it &lt;em&gt;doesn't&lt;/em&gt; always do automatically is add a B-Tree index to &lt;code&gt;user_id&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- What the ORM generated&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;"orders"&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="nv"&gt;"fk_user"&lt;/span&gt; &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="nv"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- What you actually needed for performance&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="nv"&gt;"idx_orders_user_id"&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nv"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without that index, every time you query a user's orders, Postgres has to perform a sequential scan on the &lt;code&gt;Orders&lt;/code&gt; table. At 1,000 rows, it's fine. At 10,000,000 rows, your API grinds to a halt.&lt;/p&gt;

&lt;p&gt;Because developers only look at their &lt;code&gt;schema.ts&lt;/code&gt; file, they never visually spot the missing index.&lt;/p&gt;




&lt;h3&gt;
  
  
  👁️ Stop Guessing. Look at your Database.
&lt;/h3&gt;

&lt;p&gt;I wanted a tool that let me &lt;em&gt;see&lt;/em&gt; my PostgreSQL schema and design it visually, without losing the power of raw SQL. When I couldn't find a good one that wasn't locked behind a massive enterprise paywall, I built one.&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;Tiger SQL&lt;/strong&gt; 🐯&lt;/p&gt;

&lt;p&gt;It’s a lightweight, open-source Postgres visualizer with built-in AI assistance. &lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Visual ERDs:&lt;/strong&gt; You paste your schema (or connect your DB), and it maps the relationships instantly.&lt;br&gt;
✅ &lt;strong&gt;AI-Assisted Optimization:&lt;/strong&gt; It analyzes your tables and actively warns you about missing indexes or poorly optimized queries before they hit production.&lt;br&gt;
✅ &lt;strong&gt;Zero Bloat:&lt;/strong&gt; It runs entirely in your browser/local environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 Take Control of Your SQL
&lt;/h3&gt;

&lt;p&gt;Don't let your ORM build a black box. You need to understand the underlying relational structure of your application.&lt;/p&gt;

&lt;p&gt;Tiger SQL is completely free and open-source. Fork it, run it, and find the missing indexes in your own projects:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/jacksonkasi1/tiger-sql" rel="noopener noreferrer"&gt;Fork Tiger SQL on GitHub&lt;/a&gt;&lt;/strong&gt; (&lt;code&gt;jacksonkasi1&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How do you currently visualize and audit your Postgres schemas in your team? Let me know below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>typescript</category>
      <category>architecture</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Why Your React Data Tables Are a Bloated Mess (And How to Automate Them)</title>
      <dc:creator>Jackson Kasi</dc:creator>
      <pubDate>Thu, 02 Apr 2026 18:25:58 +0000</pubDate>
      <link>https://forem.com/jacksonkasi/why-your-react-data-tables-are-a-bloated-mess-and-how-to-automate-them-54o9</link>
      <guid>https://forem.com/jacksonkasi/why-your-react-data-tables-are-a-bloated-mess-and-how-to-automate-them-54o9</guid>
      <description>&lt;p&gt;🛑 &lt;strong&gt;Building data tables in B2B SaaS is the most tedious, soul-crushing task in full-stack engineering.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Every time you need a new dashboard view, your engineers do the exact same dance:&lt;/p&gt;

&lt;p&gt;👎 &lt;strong&gt;1.&lt;/strong&gt; Write 200 lines of TanStack Table frontend boilerplate.&lt;br&gt;
👎 &lt;strong&gt;2.&lt;/strong&gt; Manage &lt;code&gt;useQuery&lt;/code&gt; state for pagination, sorting, and complex filtering.&lt;br&gt;
👎 &lt;strong&gt;3.&lt;/strong&gt; Write a backend API to parse &lt;code&gt;?column=price&amp;amp;sort=desc&amp;amp;page=2&lt;/code&gt; into something the DB understands.&lt;br&gt;
👎 &lt;strong&gt;4.&lt;/strong&gt; Write Drizzle ORM queries to manually map those query params to SQL conditions.&lt;/p&gt;

&lt;p&gt;If your team is doing this manually for every single table in your enterprise app, you are burning money and engineering hours on solved problems. &lt;/p&gt;




&lt;h3&gt;
  
  
  🔌 The Disconnect Between Client and Server
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TanStack Table&lt;/strong&gt; is a masterpiece. &lt;strong&gt;Drizzle ORM&lt;/strong&gt; is a masterpiece. &lt;br&gt;
But out of the box, they don't communicate. &lt;/p&gt;

&lt;p&gt;When a user clicks "Sort by Date" on the frontend, you have to manually map that string to an &lt;code&gt;orderBy: desc(invoices.date)&lt;/code&gt; on the backend. Multiply this by 50 tables, 10 columns each, and varying filter types (contains, equals, greater than). &lt;/p&gt;

&lt;p&gt;💥 &lt;strong&gt;The Result:&lt;/strong&gt; You end up with a massive, brittle translation layer that breaks every time a schema changes.&lt;/p&gt;




&lt;h3&gt;
  
  
  🛠️ The Engineering Solution: TableCraft
&lt;/h3&gt;

&lt;p&gt;I got tired of watching teams waste entire sprints building standard CRUD tables. So I built an engine to eliminate the translation layer.&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;TableCraft&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;TableCraft acts as the missing bridge between Drizzle ORM and TanStack Table. Instead of writing the frontend state and the backend query logic separately, TableCraft auto-generates the APIs directly from your Drizzle schema:&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Filtering&lt;/strong&gt;&lt;br&gt;
✅ &lt;strong&gt;Sorting&lt;/strong&gt;&lt;br&gt;
✅ &lt;strong&gt;Pagination&lt;/strong&gt;&lt;br&gt;
✅ &lt;strong&gt;End-to-end type safety&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You define the schema once. The engine automatically handles the REST API generation (via &lt;strong&gt;Hono&lt;/strong&gt;) and the frontend table rendering (via &lt;strong&gt;React&lt;/strong&gt;).&lt;/p&gt;




&lt;h3&gt;
  
  
  📈 Why This Matters for Scale
&lt;/h3&gt;

&lt;p&gt;When you are moving fast to find Product-Market Fit (PMF), you cannot afford to spend 5 hours building a user directory table. You need to drop the schema, render the table, and move back to writing core business logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop building pagination and sorting from scratch. It is junior-level busywork.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🚀 Try it yourself
&lt;/h3&gt;

&lt;p&gt;I open-sourced the engine. If you want to stop wasting your engineering team's time and enforce strict, type-safe table generation, check it out below:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/jacksonkasi1/TableCraft" rel="noopener noreferrer"&gt;Fork TableCraft on GitHub&lt;/a&gt;&lt;/strong&gt; (&lt;code&gt;jacksonkasi1&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Build systems, not components.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>architecture</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How to Build True Multi-Tenant Database Isolation (Stop using if-statements)</title>
      <dc:creator>Jackson Kasi</dc:creator>
      <pubDate>Thu, 02 Apr 2026 18:22:40 +0000</pubDate>
      <link>https://forem.com/jacksonkasi/how-to-build-true-multi-tenant-database-isolation-stop-using-if-statements-1402</link>
      <guid>https://forem.com/jacksonkasi/how-to-build-true-multi-tenant-database-isolation-stop-using-if-statements-1402</guid>
      <description>&lt;p&gt;🚨 &lt;strong&gt;If you are building a B2B SaaS, your biggest nightmare isn't downtime—it's a cross-tenant data leak.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Most tutorials teach you to handle multi-tenancy like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ The Junior Developer Approach&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orgId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orgId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💥 &lt;strong&gt;This is a ticking time bomb.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;It relies on the developer &lt;em&gt;remembering&lt;/em&gt; to append the &lt;code&gt;orgId&lt;/code&gt; check on every single database query. If a developer forgets it on one endpoint, Tenant A just saw Tenant B's invoices.&lt;/p&gt;

&lt;p&gt;Here is how you build true multi-tenant isolation that senior engineers actually trust.&lt;/p&gt;




&lt;h3&gt;
  
  
  🛡️ 1. The Principle of Zero Trust in the Application Layer
&lt;/h3&gt;

&lt;p&gt;Your application logic &lt;strong&gt;should not&lt;/strong&gt; be responsible for tenant isolation. The isolation must happen at the &lt;strong&gt;middleware&lt;/strong&gt; or &lt;strong&gt;database&lt;/strong&gt; level. &lt;/p&gt;

&lt;p&gt;When a request comes in, the context of &lt;em&gt;who&lt;/em&gt; is asking and &lt;em&gt;which organization&lt;/em&gt; they belong to must be established &lt;strong&gt;before&lt;/strong&gt; the route handler is even executed.&lt;/p&gt;




&lt;h3&gt;
  
  
  ⚙️ 2. The Implementation: Hono + Drizzle + Better Auth
&lt;/h3&gt;

&lt;p&gt;In modern architectures, we can leverage middleware to inject the tenant context into the request lifecycle. Here is how we handle it in our stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Validate and Extract the Tenant&lt;/strong&gt;&lt;br&gt;
Every request passes through an authentication middleware. If the token is valid, we extract the &lt;code&gt;activeOrganizationId&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ The Architect Approach (Hono Middleware)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createMiddleware&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hono/factory&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tenantAuthMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Extract session securely from cookies/headers&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;betterAuth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeOrganizationId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Failure Handling: Explicitly reject missing tenant contexts&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized: Missing organization context.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Inject the trusted org ID into the request context&lt;/span&gt;
  &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;orgId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeOrganizationId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Enforced Database Context&lt;/strong&gt;&lt;br&gt;
Now, inside your route, you don't rely on the client payload. You rely on the strictly validated context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/protected/invoices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tenantAuthMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orgId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;orgId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Guaranteed to be valid and authorized&lt;/span&gt;

  &lt;span class="c1"&gt;// The DB query relies on the trusted middleware context, not req.body&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tenantInvoices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orgId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orgId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tenantInvoices&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🚦 3. Handling Failure States
&lt;/h3&gt;

&lt;p&gt;What happens if the service-to-service call fails, or the JWT expires mid-flight?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  🔴 &lt;strong&gt;Token Expired:&lt;/strong&gt; The middleware catches the expired session and returns a &lt;code&gt;401 Unauthorized&lt;/code&gt; before hitting the database. The frontend is forced to refresh the session.&lt;/li&gt;
&lt;li&gt;  🔴 &lt;strong&gt;Tenant Mismatch:&lt;/strong&gt; If a user tries to access a resource belonging to &lt;code&gt;Org B&lt;/code&gt; but their token resolves to &lt;code&gt;Org A&lt;/code&gt;, the middleware throws a &lt;code&gt;403 Forbidden&lt;/code&gt;. The database is never touched.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🐘 4. Going Further: Row-Level Security (RLS)
&lt;/h3&gt;

&lt;p&gt;For absolute paranoia, you push this logic down into &lt;strong&gt;PostgreSQL&lt;/strong&gt; itself using &lt;strong&gt;Row-Level Security (RLS)&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;You set the Postgres session variable &lt;code&gt;app.current_tenant&lt;/code&gt; to the &lt;code&gt;orgId&lt;/code&gt; upon connection, and Postgres physically blocks any query trying to read rows outside that ID, even if the application developer blindly writes &lt;code&gt;select * from invoices&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  🎯 The Takeaway
&lt;/h3&gt;

&lt;p&gt;Stop building SaaS templates that rely on application-level &lt;code&gt;if&lt;/code&gt; statements for security. &lt;/p&gt;

&lt;p&gt;I got tired of auditing codebases with these vulnerabilities, so I built an open-source monorepo that enforces these boundaries by default. It separates the &lt;strong&gt;Vite&lt;/strong&gt; frontend from the &lt;strong&gt;Hono&lt;/strong&gt; API, uses &lt;strong&gt;Drizzle ORM&lt;/strong&gt;, and strictly isolates tenant data at the middleware level using &lt;strong&gt;Better Auth&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;If you want to see the full production implementation of this architecture, check out the &lt;code&gt;organization-v2&lt;/code&gt; branch of &lt;a href="https://github.com/jacksonkasi1/FlowStack/tree/organization-v2" rel="noopener noreferrer"&gt;FlowStack&lt;/a&gt; on my GitHub.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don't let framework magic make you lazy about security boundaries.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>webdev</category>
      <category>postgres</category>
      <category>security</category>
    </item>
    <item>
      <title>I’m sick of $200 SaaS boilerplates that leak tenant data. Here is how to build real isolation.</title>
      <dc:creator>Jackson Kasi</dc:creator>
      <pubDate>Thu, 02 Apr 2026 16:57:49 +0000</pubDate>
      <link>https://forem.com/jacksonkasi/im-sick-of-200-saas-boilerplates-that-leak-tenant-data-here-is-how-to-build-real-isolation-41oo</link>
      <guid>https://forem.com/jacksonkasi/im-sick-of-200-saas-boilerplates-that-leak-tenant-data-here-is-how-to-build-real-isolation-41oo</guid>
      <description>&lt;p&gt;🤢 &lt;strong&gt;I’m sick of "SaaS boilerplates" that charge you $200 just to wrap a framework and leave you to figure out multi-tenant database isolation yourself when your first enterprise customer signs up.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1518770660439-4636190af475%3Fauto%3Dformat%26fit%3Dcrop%26w%3D1000%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1518770660439-4636190af475%3Fauto%3Dformat%26fit%3Dcrop%26w%3D1000%26q%3D80" alt="The complexity trap" width="1000" height="667"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Most templates start clean, but turn into this within a month.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;if (user.orgId === req.body.orgId)&lt;/code&gt; checks are going to leak data eventually. It's just a matter of time. You don't need "magic" hidden in &lt;code&gt;node_modules&lt;/code&gt;. You need an explicit, boring, production-grade foundation.&lt;/p&gt;

&lt;p&gt;I got tired of rebuilding the same complex isolation architecture, so I built &lt;strong&gt;FlowStack&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Today, I’m open-sourcing the &lt;code&gt;organization-v2&lt;/code&gt; branch. &lt;strong&gt;No paywalls. No games.&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  🏗️ Why "Boring" Architecture Wins
&lt;/h3&gt;

&lt;p&gt;When you are building a B2B SaaS, your architecture needs to respect strict boundaries. FlowStack is built as a &lt;strong&gt;Turborepo&lt;/strong&gt; monorepo to enforce this separation physically, not just logically.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  📦 &lt;strong&gt;&lt;code&gt;apps/server&lt;/code&gt;&lt;/strong&gt; (The API)&lt;/li&gt;
&lt;li&gt;  🖥️ &lt;strong&gt;&lt;code&gt;apps/web&lt;/code&gt;&lt;/strong&gt; (The Client)&lt;/li&gt;
&lt;li&gt;  🔐 &lt;strong&gt;&lt;code&gt;packages/auth&lt;/code&gt;&lt;/strong&gt; (Identity &amp;amp; Isolation)&lt;/li&gt;
&lt;li&gt;  🗄️ &lt;strong&gt;&lt;code&gt;packages/db&lt;/code&gt;&lt;/strong&gt; (Type-safe Postgres Schemas)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a package doesn't need to know about the database, it doesn't get access to it. &lt;strong&gt;Zero Leakage.&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  ⚡ The Engine: Hono + Bun
&lt;/h3&gt;

&lt;p&gt;Cold starts are the enemy of good UX. By running the API on &lt;strong&gt;Hono&lt;/strong&gt; and &lt;strong&gt;Bun&lt;/strong&gt;, the server responds in milliseconds. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1507413245164-6160d8298b31%3Fauto%3Dformat%26fit%3Dcrop%26w%3D1000%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1507413245164-6160d8298b31%3Fauto%3Dformat%26fit%3Dcrop%26w%3D1000%26q%3D80" alt="High Performance Engine" width="1000" height="667"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Speed isn't a feature; it's a foundation.&lt;/em&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  🔐 True Multi-Org Isolation (Powered by Better Auth)
&lt;/h3&gt;

&lt;p&gt;Most developers spend weeks building invitation flows, password resets, and role-based access controls (RBAC). &lt;/p&gt;

&lt;p&gt;FlowStack integrates &lt;strong&gt;Better Auth&lt;/strong&gt; with custom plugins to handle this natively. Let's look at how we automatically resolve active organizations on login without exposing it to the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inside packages/auth/src/auth.ts&lt;/span&gt;
&lt;span class="nx"&gt;databaseHooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;before&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// Auto-assign the user's first organization&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userMemberships&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;memberTable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;memberTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userMemberships&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;activeOrganizationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userMemberships&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;organizationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Failed to set active organization: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures the session &lt;em&gt;always&lt;/em&gt; knows its boundaries before the first request is even processed.&lt;/p&gt;




&lt;h3&gt;
  
  
  🎨 The Frontend: React 19 + Vite 7
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1499951360447-b19be8fe80f5%3Fauto%3Dformat%26fit%3Dcrop%26w%3D1000%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1499951360447-b19be8fe80f5%3Fauto%3Dformat%26fit%3Dcrop%26w%3D1000%26q%3D80" alt="Modern Workspace" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We didn't compromise on Developer Experience (DX). &lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;React 19 &amp;amp; Vite 7&lt;/strong&gt; for immediate HMR.&lt;br&gt;
✅ &lt;strong&gt;TanStack Start&lt;/strong&gt; for flawless routing.&lt;br&gt;
✅ &lt;strong&gt;Tailwind CSS v4&lt;/strong&gt; for the styling engine.&lt;/p&gt;


&lt;h3&gt;
  
  
  🚀 Try It Yourself
&lt;/h3&gt;

&lt;p&gt;If you want magic "one-click" illusions that break in production, go buy a template. &lt;/p&gt;

&lt;p&gt;If you want a production-ready monorepo that won't make you rewrite your entire auth logic in 6 months, &lt;strong&gt;fork FlowStack.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;-b&lt;/span&gt; organization-v2 https://github.com/jacksonkasi1/FlowStack.git
bun &lt;span class="nb"&gt;install
&lt;/span&gt;bun dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⭐ &lt;strong&gt;Star the repo if this saves you 100+ hours of architectural headaches!&lt;/strong&gt;&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://github.com/jacksonkasi1/FlowStack/tree/organization-v2" rel="noopener noreferrer"&gt;GitHub: FlowStack/organization-v2&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Let me know in the comments how you handle tenant isolation in your current stack!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>architecture</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
