Skip to content

stackpress/inquire

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

170 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Inquire

NPM Package Tests Status Coverage Status Commits License

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.

What Inquire is for

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

Supported databases

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.

Requirements

  • Node.js 22 or newer
  • yarn

Install

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/pglite

Quick start

This 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);

Connection examples

MySQL

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);

PostgreSQL

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);

PGlite

import { PGlite } from '@electric-sql/pglite';
import connect from '@stackpress/inquire-pglite';

const resource = new PGlite('./build/database');
const engine = connect(resource);

Core API

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?)

Raw SQL

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'}
`;

Transactions

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.

Type safety

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');

Documentation

The full docs now live under specs:

Examples

For package-level usage examples, see:

About

Generic Typed SQL Builders

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors