Inquire is a lightweight TypeScript SQL builder. It helps you define schemas, build SQL queries, and execute them through a connection wrapper. It does not model records, relationships, or unit-of-work behavior for you.
Use Inquire when you want:
- typed query results in TypeScript
- one small API for MySQL, PostgreSQL, SQLite, and PGlite
- schema helpers without adopting a full ORM
- the option to mix builders and raw SQL
Use something else if you want:
- model instances and change tracking
- relationship loading
- repository patterns generated by the library
- a full migration workflow with history and orchestration
Inquire supports these connection packages:
@stackpress/inquire-mysql2@stackpress/inquire-pg@stackpress/inquire-pglite@stackpress/inquire-sqlite3
PostgreSQL-compatible services such as CockroachDB, Neon, Supabase, and Vercel Postgres can use the PostgreSQL adapter when the underlying driver is compatible.
- Node.js 22 or newer
yarn
Install the core library and one connection package.
# Core package
yarn add @stackpress/inquire
# MySQL
yarn add @stackpress/inquire-mysql2 mysql2
# PostgreSQL
yarn add @stackpress/inquire-pg pg
# SQLite
yarn add @stackpress/inquire-sqlite3 better-sqlite3
# PGlite
yarn add @stackpress/inquire-pglite @electric-sql/pgliteThis is the shortest local path with SQLite.
import sqlite from 'better-sqlite3';
import connect from '@stackpress/inquire-sqlite3';
const resource = sqlite(':memory:');
const engine = connect(resource);
await engine.create('users')
.addField('id', { type: 'integer', autoIncrement: true })
.addField('email', { type: 'string', length: 255, nullable: false })
.addField('name', { type: 'string', length: 255, nullable: false })
.addPrimaryKey('id')
.addUniqueKey('users_email_unique', 'email');
await engine.insert('users').values({
email: 'ada@example.com',
name: 'Ada'
});
type UserRow = {
id: number;
email: string;
name: string;
};
const users = await engine.select<UserRow>([
'id',
'email',
'name'
])
.from('users')
.where('email = ?', ['ada@example.com']);
console.log(users);import mysql from 'mysql2/promise';
import connect from '@stackpress/inquire-mysql2';
const resource = await mysql.createConnection({
host: 'localhost',
user: 'root',
database: 'app'
});
const engine = connect(resource);import { Client } from 'pg';
import connect from '@stackpress/inquire-pg';
const client = new Client({
database: 'app',
user: 'postgres'
});
await client.connect();
const engine = connect(client);import { PGlite } from '@electric-sql/pglite';
import connect from '@stackpress/inquire-pglite';
const resource = new PGlite('./build/database');
const engine = connect(resource);The Engine creates six main builders:
create(table)alter(table)select(columns?)insert(table)update(table)delete(table)
It also exposes:
query(query, values?)sql\...``transaction(callback)diff(from, to)rename(from, to)drop(table)truncate(table, cascade?)
Use query() or sql when the builder surface is not enough.
const rows = await engine.query<{ id: number; email: string }>(
'SELECT id, email FROM users WHERE id = ?',
[1]
);
const ids = await engine.sql<{ id: number }>`
SELECT id
FROM users
WHERE email LIKE ${'%@example.com'}
`;Use engine.transaction() when several writes must succeed or fail together.
await engine.transaction(async (tx) => {
await tx.query({
query: 'INSERT INTO users (email, name) VALUES (?, ?)',
values: ['grace@example.com', 'Grace']
});
await tx.query({
query: 'INSERT INTO profiles (user_id) VALUES (?)',
values: [1]
});
});The transaction callback receives the connection wrapper, not a nested Engine.
Use TypeScript generics to type query results.
type User = {
id: number;
email: string;
name: string;
};
const users = await engine.select<User>([
'id',
'email',
'name'
])
.from('users');The full docs now live under specs:
For package-level usage examples, see: