Tinqer API Reference
Reference for adapter execution helpers, typed contexts, and query utilities.
Table of Contents
1. Execution APIs
Adapter packages export the runtime helpers that turn expression trees into SQL and execute them. PostgreSQL helpers live in @webpods/tinqer-sql-pg-promise
; SQLite helpers live in @webpods/tinqer-sql-better-sqlite3
and expose the same signatures.
1.1 selectStatement
Converts a query builder function into SQL and named parameters without executing it. The query builder receives a DSL context, parameters, and helper functions.
Signature
function selectStatement<TSchema, TParams, TResult>(
schema: DatabaseSchema<TSchema>,
queryBuilder: (
ctx: QueryBuilder<TSchema>,
params: TParams,
helpers: QueryHelpers,
) => Queryable<TResult> | OrderedQueryable<TResult> | TerminalQuery<TResult>,
params: TParams,
): SqlResult<TParams & Record<string, unknown>, TResult>;
Example (PostgreSQL)
import { createSchema } from "@webpods/tinqer";
import { selectStatement } from "@webpods/tinqer-sql-pg-promise";
interface Schema {
users: { id: number; name: string; age: number };
}
const schema = createSchema<Schema>();
const { sql, params } = selectStatement(
schema,
(q, p) =>
q
.from("users")
.where((u) => u.age >= p.minAge)
.select((u) => ({ id: u.id, name: u.name })),
{ minAge: 18 },
);
// sql: SELECT "id" AS "id", "name" AS "name" FROM "users" WHERE "age" >= $(minAge)
// params: { minAge: 18 }
1.2 executeSelect
Executes a query builder against the database and returns typed results. The query builder receives a DSL context, parameters, and helper functions.
async function executeSelect<
TSchema,
TParams,
TQuery extends Queryable<unknown> | OrderedQueryable<unknown> | TerminalQuery<unknown>,
>(
db: PgDatabase | BetterSqlite3Database,
schema: DatabaseSchema<TSchema>,
queryBuilder: (ctx: QueryBuilder<TSchema>, params: TParams, helpers: QueryHelpers) => TQuery,
params: TParams,
options?: ExecuteOptions,
): Promise<
TQuery extends Queryable<infer T>
? T[]
: TQuery extends OrderedQueryable<infer T>
? T[]
: TQuery extends TerminalQuery<infer T>
? T
: never
>;
Example (PostgreSQL)
import { createSchema } from "@webpods/tinqer";
import { executeSelect } from "@webpods/tinqer-sql-pg-promise";
interface Schema {
users: { id: number; name: string; age: number };
}
const schema = createSchema<Schema>();
const users = await executeSelect(
db,
schema,
(q, p) =>
q
.from("users")
.where((u) => u.age >= p.minAge)
.orderBy((u) => u.name),
{ minAge: 21 },
);
1.3 executeSelectSimple
Convenience wrapper for queries that do not need external parameters. The query builder receives a DSL context, an empty params object, and helper functions.
async function executeSelectSimple<
TSchema,
TQuery extends Queryable<unknown> | OrderedQueryable<unknown> | TerminalQuery<unknown>,
>(
db: PgDatabase | BetterSqlite3Database,
schema: DatabaseSchema<TSchema>,
queryBuilder: (
ctx: QueryBuilder<TSchema>,
params: Record<string, never>,
helpers: QueryHelpers,
) => TQuery,
options?: ExecuteOptions,
): Promise<
TQuery extends Queryable<infer T>
? T[]
: TQuery extends OrderedQueryable<infer T>
? T[]
: TQuery extends TerminalQuery<infer T>
? T
: never
>;
Example
import { createSchema } from "@webpods/tinqer";
import { executeSelectSimple } from "@webpods/tinqer-sql-pg-promise";
interface Schema {
users: { id: number; name: string };
}
const schema = createSchema<Schema>();
const allUsers = await executeSelectSimple(db, schema, (q) => q.from("users"));
1.4 insertStatement & executeInsert
Generate and execute INSERT statements with optional RETURNING clauses. The query builder receives a DSL context, parameters, and helper functions.
function insertStatement<TSchema, TParams, TTable, TReturning = never>(
schema: DatabaseSchema<TSchema>,
queryBuilder: (
ctx: QueryBuilder<TSchema>,
params: TParams,
helpers: QueryHelpers,
) => Insertable<TTable> | InsertableWithReturning<TTable, TReturning>,
params: TParams,
): SqlResult<TParams & Record<string, unknown>, TReturning extends never ? void : TReturning>;
async function executeInsert<TSchema, TParams, TTable>(
db: PgDatabase | BetterSqlite3Database,
schema: DatabaseSchema<TSchema>,
queryBuilder: (
ctx: QueryBuilder<TSchema>,
params: TParams,
helpers: QueryHelpers,
) => Insertable<TTable>,
params: TParams,
options?: ExecuteOptions,
): Promise<number>;
async function executeInsert<TSchema, TParams, TTable, TReturning>(
db: PgDatabase | BetterSqlite3Database,
schema: DatabaseSchema<TSchema>,
queryBuilder: (
ctx: QueryBuilder<TSchema>,
params: TParams,
helpers: QueryHelpers,
) => InsertableWithReturning<TTable, TReturning>,
params: TParams,
options?: ExecuteOptions,
): Promise<TReturning[]>;
Example
import { createSchema } from "@webpods/tinqer";
import { executeInsert } from "@webpods/tinqer-sql-pg-promise";
interface Schema {
users: { id: number; name: string };
}
const schema = createSchema<Schema>();
const inserted = await executeInsert(
db,
schema,
(q) => q.insertInto("users").values({ name: "Alice" }),
{},
);
const createdUsers = await executeInsert(
db,
schema,
(q) =>
q
.insertInto("users")
.values({ name: "Bob" })
.returning((u) => ({ id: u.id, name: u.name })),
{},
);
1.5 updateStatement & executeUpdate
Generate and execute UPDATE statements with optional RETURNING clauses. The query builder receives a DSL context, parameters, and helper functions.
function updateStatement<TSchema, TParams, TTable, TReturning = never>(
schema: DatabaseSchema<TSchema>,
queryBuilder: (
ctx: QueryBuilder<TSchema>,
params: TParams,
helpers: QueryHelpers,
) =>
| UpdatableWithSet<TTable>
| UpdatableComplete<TTable>
| UpdatableWithReturning<TTable, TReturning>,
params: TParams,
): SqlResult<TParams & Record<string, unknown>, TReturning extends never ? void : TReturning>;
async function executeUpdate<TSchema, TParams, TTable>(
db: PgDatabase | BetterSqlite3Database,
schema: DatabaseSchema<TSchema>,
queryBuilder: (
ctx: QueryBuilder<TSchema>,
params: TParams,
helpers: QueryHelpers,
) => UpdatableWithSet<TTable> | UpdatableComplete<TTable>,
params: TParams,
options?: ExecuteOptions,
): Promise<number>;
async function executeUpdate<TSchema, TParams, TTable, TReturning>(
db: PgDatabase | BetterSqlite3Database,
schema: DatabaseSchema<TSchema>,
queryBuilder: (
ctx: QueryBuilder<TSchema>,
params: TParams,
helpers: QueryHelpers,
) => UpdatableWithReturning<TTable, TReturning>,
params: TParams,
options?: ExecuteOptions,
): Promise<TReturning[]>;
Example
import { createSchema } from "@webpods/tinqer";
import { executeUpdate } from "@webpods/tinqer-sql-pg-promise";
interface Schema {
users: { id: number; name: string; lastLogin: Date; status: string };
}
const schema = createSchema<Schema>();
const updatedRows = await executeUpdate(
db,
schema,
(q, p) =>
q
.update("users")
.set({ status: "inactive" })
.where((u) => u.lastLogin < p.cutoff),
{ cutoff: new Date("2024-01-01") },
);
1.6 deleteStatement & executeDelete
Generate and execute DELETE statements. The query builder receives a DSL context, parameters, and helper functions.
function deleteStatement<TSchema, TParams, TResult>(
schema: DatabaseSchema<TSchema>,
queryBuilder: (
ctx: QueryBuilder<TSchema>,
params: TParams,
helpers: QueryHelpers,
) => Deletable<TResult> | DeletableComplete<TResult>,
params: TParams,
): SqlResult<TParams & Record<string, unknown>, void>;
async function executeDelete<TSchema, TParams, TResult>(
db: PgDatabase | BetterSqlite3Database,
schema: DatabaseSchema<TSchema>,
queryBuilder: (
ctx: QueryBuilder<TSchema>,
params: TParams,
helpers: QueryHelpers,
) => Deletable<TResult> | DeletableComplete<TResult>,
params: TParams,
options?: ExecuteOptions,
): Promise<number>;
Example
import { createSchema } from "@webpods/tinqer";
import { executeDelete } from "@webpods/tinqer-sql-pg-promise";
interface Schema {
users: { id: number; name: string; status: string };
}
const schema = createSchema<Schema>();
const deletedCount = await executeDelete(
db,
schema,
(q) => q.deleteFrom("users").where((u) => u.status === "inactive"),
{},
);
1.7 Statement Functions (selectStatement, insertStatement, etc.)
Generate SQL and parameters without executing them. These functions are useful for debugging, testing, or when you need the SQL before execution.
function selectStatement<TSchema, TResult>(
schema: DatabaseSchema<TSchema>,
builder: (q: QueryBuilder<TSchema>) => Queryable<TResult>,
): SqlResult<Record<string, unknown>, TResult>;
Example (SQLite)
import { createSchema } from "@webpods/tinqer";
import { selectStatement } from "@webpods/tinqer-sql-better-sqlite3";
interface Schema {
products: { id: number; name: string; price: number; inStock: number };
}
const schema = createSchema<Schema>();
// Generate SQL without executing
const result = selectStatement(schema, (q) =>
q
.from("products")
.where((p) => p.inStock === 1)
.orderByDescending((p) => p.price),
);
// result.sql contains the SQL string
// result.params contains the parameters
1.8 ExecuteOptions & SqlResult
Both adapters expose ExecuteOptions
and SqlResult
for inspection and typing.
interface ExecuteOptions {
onSql?: (result: SqlResult<Record<string, unknown>, unknown>) => void;
}
interface SqlResult<TParams, TResult> {
sql: string;
params: TParams;
_resultType?: TResult; // phantom type information
}
Use onSql
for logging, testing, or debugging without changing execution flow.
2. Type-Safe Contexts
2.1 createSchema
Creates a phantom-typed DatabaseSchema
that ties table names to row types. The schema is passed to execution functions, which provide a type-safe query builder through the lambda’s first parameter.
import { createSchema } from "@webpods/tinqer";
import { executeSelect } from "@webpods/tinqer-sql-pg-promise";
interface Schema {
users: { id: number; name: string; email: string };
posts: { id: number; userId: number; title: string };
}
const schema = createSchema<Schema>();
// Schema is passed to execute functions, which provide the query builder 'q' parameter
const results = await executeSelect(db, schema, (q) =>
q.from("users").where((u) => u.email.endsWith("@example.com")),
);
3. Helper Utilities
3.1 createQueryHelpers
Provides helper functions for case-insensitive comparisons and string searches. Helpers are automatically passed as the third parameter to query builder functions.
import { createSchema, createQueryHelpers } from "@webpods/tinqer";
import { selectStatement } from "@webpods/tinqer-sql-pg-promise";
interface Schema {
users: { id: number; name: string };
}
const schema = createSchema<Schema>();
const result = selectStatement(
schema,
(q, params, helpers) =>
q.from("users").where((u) => helpers.functions.icontains(u.name, "alice")),
{},
);
Available Helper Functions
Helpers expose the following functions that adapt per database dialect:
ilike(field, pattern)
- Case-insensitive LIKE comparisoncontains(field, substring)
- Check if field contains substring (case-sensitive)icontains(field, substring)
- Check if field contains substring (case-insensitive)startsWith(field, prefix)
- Check if field starts with prefix (case-sensitive)istartsWith(field, prefix)
- Check if field starts with prefix (case-insensitive)endsWith(field, suffix)
- Check if field ends with suffix (case-sensitive)iendsWith(field, suffix)
- Check if field ends with suffix (case-insensitive)
Creating Custom Helpers
You can create helpers with custom functions:
const helpers = createQueryHelpers<Schema>();
// Use helpers in your queries through the third parameter