VibORM

L6 - Query Engine

Transform VibORM queries into SQL without knowing which database you're using

Location: src/query-engine/

Why This Layer Exists

VibORM supports PostgreSQL, MySQL, and SQLite. Each has different syntax:

-- PostgreSQL
SELECT COALESCE(json_agg(row_to_json(t)), '[]'::json) FROM ...

-- MySQL  
SELECT JSON_ARRAYAGG(JSON_OBJECT(...)) FROM ...

-- SQLite
SELECT json_group_array(json_object(...)) FROM ...

If the query engine generated dialect-specific SQL, adding a database would require modifying 50+ files. Instead:

  • Query engine decides WHAT to query (structure, joins, conditions)
  • Adapters decide HOW to express it (syntax, functions, quotes)

The Golden Rule

Query engine NEVER generates dialect-specific SQL. ALWAYS delegate to adapter.

// ❌ WRONG: Hardcoded PostgreSQL
sql`COALESCE(json_agg(...), '[]'::json)`

// ✅ RIGHT: Delegate to adapter
ctx.adapter.json.agg(subquery)

This is the most important architectural rule in VibORM.

How It Works

1. Query Context

Every query operation receives a context:

interface QueryContext {
  adapter: DatabaseAdapter;  // Database-specific methods
  model: Model;              // The model being queried
  // ...
}

2. SQL Fragments

Builders return Sql fragments, not strings:

const fragment = sql`WHERE ${field} = ${value}`;
// Contains: template strings + parameter values (separated)

Fragments enable:

  • Parameterization: Values stay separate from SQL (prevents injection)
  • Composition: Fragments can nest inside other fragments

3. Operation Flow

orm.user.findMany({ where: { email: "..." } })

// 1. Query enters operations/findMany.ts
// 2. Builders construct SQL fragments
// 3. Fragments delegate to adapter for syntax
// 4. Driver executes final SQL

Core Operations

OperationDescription
findManySELECT with relations
findFirstSELECT with LIMIT 1
findUniqueSELECT by unique constraint
createINSERT single row
createManyINSERT multiple rows
updateUPDATE by condition
updateManyUPDATE multiple rows
deleteDELETE by condition
deleteManyDELETE multiple rows
countCOUNT with conditions
aggregateSUM, AVG, MIN, MAX

Builder Responsibilities

BuilderPurpose
selectColumn selection, aliasing
whereWHERE clause conditions
orderByORDER BY clause
paginationLIMIT, OFFSET
joinJOIN clauses for relations
includeNested relation loading
createINSERT statements
updateUPDATE statements

Connection to Other Layers

  • L3 (Query Schemas): Query engine validates inputs against schemas
  • L7 (Adapters): All SQL syntax delegated to adapters
  • L8 (Drivers): Drivers execute the generated SQL
  • L9 (Client): Client calls query engine operations

On this page