Back to Insights
Development5 min read

How I Built MountSQLI

SA
Shekh Saheb Ali
Aug 4, 2026
5 min read
How I Built MountSQLI

This started as a small wrapper around a database driver. It grew into an ORM with a compiler inside it. Let me explain how it works in plain words.

The Core Idea: Queries Become A Plan

Most ORMs build a query by chaining objects, then turn that object into SQL at the end. MountSQLI does the opposite. Every query is turned into a plan — a plain, structured list of actions. The plan is compiled first, then executed.

That plain plan is called the QueryPlan, and it's the thing everything else builds on.

Here is what it looks like in practice:

const db = await mountsqli({
  driver: 'sqlite',
  url: ':memory:',
  tables: [users],
})

const rows = await db.query(users).select('id', 'email')

When you write .select('id', 'email'), two things happen:

  1. The type system knows you only want those two columns, and it stops you from using any others.
  2. The compiler turns the query into a plan, validates it, and only then runs it.

The database is never asked to make sense of garbage, because the compiler rejects bad queries at build time.

How It's Split Into Packages

A single library got too big, so I split it into small packages, each published separately:

  • core — the umbrella that ties everything together.
  • schema — how you define tables and columns, with types.
  • compiler — the QueryPlan IR, the dialects (sqlite / postgres / mysql), and query optimization.
  • query — the friendly typed builder you actually write against.
  • driver-* — one small driver per database.
  • migration — figures out schema changes and writes the SQL for them.
  • auth — passwords, tokens, permissions.
  • ai — turns plain English into SQL, then sends it through the same validator.

The compiler is the important part. Everything else just uses it.

Why Compile-To-A-Plan Helps

Because the query is data before it's SQL, three useful things follow:

  • Smaller code. If you only use one database, parts you don't need get dropped at build time.
  • Works on the edge. The plan can travel to a lightweight runtime (like a serverless function) because it's just data, not a web of connected objects.
  • No junk objects. The executor walks the plan directly, so it creates almost no throwaway objects. That matters on hot paths that run for every request.

It Checks AI-Generated SQL Too

Here is the part I like most: because every query goes through the same compiler, SQL produced by an AI is validated the same way as SQL written by hand. If an LLM gives you a bad query, the compiler catches it before it touches your database. The ai package turns plain English into SQL, and that SQL still has to pass the compiler like anything else.

Two Things I Wished I Knew

  • Make the IR the star. The builder is just the nice surface. Almost all the testing and effort belongs in the compiler and executor, because they carry the correctness.
  • Types are the best feature. The number one thing MountSQLI gives you is that most mistakes are impossible to even write. That's the compiler's job — stop bad code before it runs — and that one choice is worth more than any feature list.

The full source is on GitHub under MIT if you want to look around.

Share
#ORM#Database#Compilers