@axiomdb/runtime
v0.1.1
Published
SQL compiler runtime for AxiomDB with dialect support
Readme
@axiomdb/runtime
SQL compiler runtime for AxiomDB. Compiles query builder AST into parameterized SQL, provides database clients for PostgreSQL, MySQL, and MSSQL, and handles dialect-specific placeholder styles and type coercion.
Installation
npm install @axiomdb/runtimeInstall the driver for your database (these are optional peer dependencies — only the one you use is required):
# PostgreSQL
npm install pg
# MySQL
npm install mysql2
# MSSQL
npm install mssqlAPI Reference
Table of Contents
- Database Clients
- Dialect Tags
- Query Execution
- Compiler API
- Standalone table() and configure()
- Dialect Modules
- Batch Execution
- Row Coercion
- Re-exported Core API
1. Database Clients
Each database has a dedicated client class that wraps its native driver pool.
1.1 PostgreSQL — ClientPG
import { Pool } from "pg";
import { ClientPG } from "@axiomdb/runtime";
const client = new ClientPG({
pool: new Pool({ connectionString: "postgresql://user:pass@localhost:5432/mydb" }),
});Constructor options:
| Option | Type | Description |
|--------|------|-------------|
| pool | pg.Pool \| pg.Client | A pg pool or client instance |
| logLevel | 'none' \| 'error' \| 'debug' | Query logging level (default: 'error') |
1.2 MySQL — ClientMySQL
import mysql from "mysql2/promise";
import { ClientMySQL } from "@axiomdb/runtime";
const client = new ClientMySQL({
pool: mysql.createPool({ host: "localhost", database: "mydb" }),
});Constructor options:
| Option | Type | Description |
|--------|------|-------------|
| pool | mysql2.Pool | A mysql2/promise pool instance |
| logLevel | 'none' \| 'error' \| 'debug' | Query logging level (default: 'error') |
1.3 MSSQL — ClientMSSQL
import sql from "mssql";
import { ClientMSSQL } from "@axiomdb/runtime";
const pool = await sql.connect({ server: "localhost", database: "mydb" });
const client = new ClientMSSQL({ pool });Constructor options:
| Option | Type | Description |
|--------|------|-------------|
| pool | mssql.ConnectionPool | A mssql connection pool instance |
| logLevel | 'none' \| 'error' \| 'debug' | Query logging level (default: 'error') |
2. Dialect Tags
The primary way to execute SQL is through dialect-specific tagged template methods on a client instance. These require the @axiomdb/babel-plugin to transform the templates at build time.
2.1 PostgreSQL
const users = await client.psql`
SELECT id, name FROM users WHERE active = ${true}
`;
// Compiles to: SELECT id, name FROM users WHERE active = $1
// params: [true]2.2 MySQL
const users = await client.mysql`
SELECT id, name FROM users WHERE active = ${true}
`;
// Compiles to: SELECT id, name FROM users WHERE active = ?
// params: [true]2.3 MSSQL
const users = await client.mssql`
SELECT id, name FROM users WHERE active = ${true}
`;
// Compiles to: SELECT id, name FROM users WHERE active = @p1
// params: [true]Values are never interpolated into the SQL string — they always become parameterized placeholders.
3. Query Execution
3.1 client.__execute(queryOrText, params?)
Execute a query builder chain or a raw SQL string. This is what the Babel plugin compiles dialect tags into.
// With a query builder
const query = table("users").select("*").where({ column: "active", op: EQ, value: true });
const rows = await client.__execute(query);
// With raw SQL (placeholders use $_ which are resolved to dialect-specific style)
const rows = await client.__execute("SELECT * FROM users WHERE id = $_", [42]);Parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| textOrBuilder | QueryBuilder \| string | A query builder instance or SQL string |
| rawParams | unknown[] | Parameters (only for string queries) |
Returns: Promise<Record<string, unknown>[]> — array of row objects.
3.2 client.__executeBatch(text, params)
Execute a multi-statement SQL string. Statements are separated by semicolons.
const results = await client.__executeBatch(
"INSERT INTO users (name) VALUES ($_); SELECT * FROM users WHERE name = $_",
["Alice", "Alice"]
);
// results[0] = rows from INSERT (if RETURNING)
// results[1] = rows from SELECTReturns: Promise<Record<string, unknown>[][]> — array of result sets, one per statement.
4. Compiler API
4.1 createCompiler(dialect)
Create a standalone compiler for a specific dialect. Useful when you need to compile queries without a client.
import { createCompiler, postgres } from "@axiomdb/runtime";
const compiler = createCompiler(postgres);
const { text, params } = compiler.compile(query.toAST());
// text: "SELECT id, name FROM users WHERE active = $1"
// params: [true]Parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| dialect | DialectCompiler | A dialect module (postgres, mysql, or mssql) |
Returns: Compiler — object with a compile(node) method that returns { text: string, params: unknown[] }.
4.2 CompileContext
Low-level compilation context used by dialect implementations. Manages parameter tracking and identifier quoting.
import { CompileContext, postgres } from "@axiomdb/runtime";
const ctx = new CompileContext(postgres);
const sql = ctx.compileNode(query.toAST());
const params = ctx.getParams();| Method | Description |
|--------|-------------|
| addParam(value) | Add a parameter and return its placeholder string |
| getParams() | Return all collected parameters |
| getDialect() | Return the dialect compiler |
| quoteId(name) | Quote a single identifier |
| quoteColumnRef(ref) | Quote a dotted column reference (e.g. "users"."id") |
| compileNode(node) | Compile a query AST node to SQL |
5. Standalone table() and configure()
For use without a client class — configure a default dialect and optionally a database adapter, then use table() directly.
5.1 configure(dialect)
Set the default dialect for standalone table() calls. Must be called before using table().
import { configure, postgres } from "@axiomdb/runtime";
configure(postgres);5.2 connect(db)
Set a default database adapter for .execute() calls.
import { configure, connect, postgres } from "@axiomdb/runtime";
import { Pool } from "pg";
configure(postgres);
const pool = new Pool({ connectionString: "..." });
connect({
query: async (text, params) => {
const result = await pool.query(text, params);
return { rows: result.rows, fields: result.fields };
},
});5.3 table(name, options?)
Create a query builder bound to the configured dialect. Returns an ExecutableQueryBuilder with .build() and .execute() methods.
import { configure, postgres, table, EQ } from "@axiomdb/runtime";
configure(postgres);
const query = table("users")
.select({ id: true, name: true })
.where({ column: "active", op: EQ, value: true });
// Compile to SQL
const { text, params } = query.build();
// text: "SELECT id, name FROM users WHERE active = $1"
// params: [true]
// Or execute directly (requires connect() or passing an adapter)
const rows = await query.execute();5.4 withCTEs(ctes, body)
Create a CTE query bound to the configured dialect.
import { configure, postgres, table, withCTEs, GT } from "@axiomdb/runtime";
configure(postgres);
const recent = table("users")
.select({ id: true, name: true })
.where({ column: "created_at", op: GT, value: cutoff })
.asCTE("recent_users");
const query = withCTEs([recent], recent.select("*").limit(10));
const { text, params } = query.build();6. Dialect Modules
Three built-in dialect modules handle SQL generation differences:
import { postgres, mysql, mssql } from "@axiomdb/runtime";| Dialect | Placeholder style | Identifier quoting | Notes |
|---------|------------------|-------------------|-------|
| postgres | $1, $2, ... | "identifier" | Supports row coercion for int8, numeric, bool |
| mysql | ? | `identifier` | Supports row coercion for NEWDECIMAL, LONGLONG |
| mssql | @p1, @p2, ... | [identifier] | Uses request.input() for parameter binding |
Each dialect implements the full DialectCompiler interface with compile methods for all query types (SELECT, INSERT, UPDATE, DELETE, MERGE), clause types (WHERE, JOIN, GROUP BY, ORDER BY, etc.), and SQL primitives (placeholders, identifier quoting, row coercion).
7. Batch Execution
Multi-statement queries are supported through __executeBatch(). Behavior varies by driver:
PostgreSQL
Statements are split and executed sequentially within a single client connection (acquired from the pool). Each statement's $N placeholders are re-indexed to $1..$K with only the referenced params.
MySQL
Requires multiStatements: true on the pool. The mysql2 driver returns multiple result sets natively.
MSSQL
The mssql driver handles multi-statement batches natively via request.query(), returning recordsets as an array.
8. Row Coercion
Drivers return some SQL types as strings (e.g. BIGINT, NUMERIC). Each dialect's coerceRow() function automatically converts these to JavaScript numbers or booleans based on field metadata:
| SQL type | JS type | Dialects |
|----------|---------|----------|
| int8 / BIGINT / LONGLONG | number | PostgreSQL, MySQL, MSSQL |
| numeric / NEWDECIMAL | number | PostgreSQL, MySQL |
| bool / BIT | boolean | PostgreSQL, MSSQL |
Coercion happens automatically when using a client class — no configuration needed.
9. Re-exported Core API
The runtime package re-exports everything from @axiomdb/core for convenience, so you only need a single import:
import {
// Operators
EQ, NEQ, GT, GTE, LT, LTE, IN, NOT_IN, LIKE, ILIKE,
BETWEEN, IS_NULL, IS_NOT_NULL, IS_TRUE, IS_FALSE,
CONTAINS, OVERLAPS, ANY, SIMILAR_TO, POSIX_MATCH,
// Combinators
and, or, not,
// Aggregate functions
COUNT, SUM, AVG, MIN, MAX, ARRAY_AGG, JSON_AGG, STRING_AGG,
// Window functions
ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, FIRST_VALUE, LAST_VALUE,
// Query builder
QueryBuilder, RawExpression, raw,
// Client classes
ClientPG, ClientMySQL, ClientMSSQL,
// Compiler
createCompiler, CompileContext,
// Dialects
postgres, mysql, mssql,
// Standalone API
configure, connect, table, withCTEs,
extendBuilder,
} from "@axiomdb/runtime";See the @axiomdb/core README for the full query builder API reference (SELECT, WHERE, JOIN, GROUP BY, INSERT, UPDATE, DELETE, CTEs, subqueries, window functions, set operations, and more).
Types
DatabaseAdapter
Interface for providing a custom database adapter:
interface DatabaseAdapter {
query(text: string, params: unknown[]): Promise<{
rows: Record<string, unknown>[];
fields?: unknown[];
}>;
queryBatch?(text: string, params: unknown[]): Promise<{
resultSets: Array<{ rows: Record<string, unknown>[]; fields?: unknown[] }>;
}>;
}DialectCompiler
Interface for implementing a custom dialect. See the built-in postgres, mysql, and mssql modules for reference implementations.
ExecutableQueryBuilder
Extends QueryBuilder from @axiomdb/core with two additional methods:
| Method | Returns | Description |
|--------|---------|-------------|
| build() | { text: string, params: unknown[] } | Compile the query to SQL |
| execute(db?) | Promise<TResult[]> | Compile and execute against a database adapter |
All chaining methods (.select(), .where(), .join(), etc.) return ExecutableQueryBuilder, so .build() and .execute() are always available at the end of the chain.
License
MIT
