npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@gibme/sql

v22.0.0

Published

Unified TypeScript database abstraction for MySQL, MariaDB, PostgreSQL, and SQLite with connection pooling, transactions, and bulk operations

Readme

Simple SQL Helpers for MySQL, MariaDB, SQLite, & Postgres

A TypeScript database abstraction library providing a unified interface for MySQL, MariaDB, PostgreSQL, and SQLite. It wraps driver-specific behavior behind a common abstract Database class so consumers use the same API regardless of backend.

Node >= 22 required.

Documentation

https://gibme-npm.github.io/sql/

Installation

yarn add @gibme/sql

Quick Start

Factory Function

Use createConnection() to instantiate the correct driver based on Database.Type:

import { createConnection, Database } from '@gibme/sql';

const client = createConnection(Database.Type.SQLITE, {
    filename: ':memory:'
});

await client.createTable('test', [
    { name: 'id', type: 'integer' },
    { name: 'value', type: 'varchar(255)' }
], ['id']);

const [rows, meta] = await client.query('SELECT * FROM test');

When no arguments are provided, createConnection() reads from environment variables (see Environment Variables below) and defaults to an in-memory SQLite database.

Direct Imports

Import specific drivers directly to avoid bundling unused drivers:

import MySQL from '@gibme/sql/mysql';
import MariaDB from '@gibme/sql/mariadb';
import Postgres from '@gibme/sql/postgres';
import SQLite from '@gibme/sql/sqlite';
import { Database } from '@gibme/sql/database';

Driver Configuration

MySQL

import MySQL from '@gibme/sql/mysql';

const client = new MySQL({
    host: 'localhost',       // default: '127.0.0.1'
    port: 3306,              // default: 3306
    user: 'someuser',        // default: ''
    password: 'somepassword',
    database: 'somedatabase',
    connectTimeout: 30_000,  // default: 30000 ms
    useSSL: false,           // default: false
    rejectUnauthorized: false // default: false
});

The MySQL driver accepts all mariadb.PoolConfig options in addition to the ones listed above.

The second constructor argument accepts a table options string used when creating tables (default: 'ENGINE=InnoDB PACK_KEYS=1 ROW_FORMAT=COMPRESSED').

MariaDB

MariaDB extends the MySQL driver and accepts the same configuration. The only difference is the UPSERT dialect used internally.

import MariaDB from '@gibme/sql/mariadb';

const client = new MariaDB({
    host: 'localhost',
    port: 3306,
    user: 'someuser',
    password: 'somepassword',
    database: 'somedatabase'
});

Postgres

import Postgres from '@gibme/sql/postgres';

const client = new Postgres({
    host: 'localhost',       // default: '127.0.0.1'
    port: 5432,              // default: 5432
    user: 'someuser',        // default: ''
    password: 'somepassword',
    database: 'somedatabase',
    ssl: false,              // default: false
    rejectUnauthorized: false // default: false
});

The Postgres driver accepts all pg.PoolConfig options in addition to the ones listed above.

SQLite

import SQLite from '@gibme/sql/sqlite';

const client = new SQLite({
    filename: './data.db', // default: ':memory:'
    readonly: false,       // default: false
    foreignKeys: true,     // default: true (enables PRAGMA foreign_keys)
    WALmode: true          // default: true (enables PRAGMA journal_mode=WAL)
});

SQLite instances are managed as singletons per filename, so multiple SQLite instances pointing to the same file share the underlying connection. All operations are serialized through a mutex for thread safety.

PRAGMA Support

Read and set PRAGMA values on SQLite connections:

const walMode = await client.getPragma('journal_mode');
await client.setPragma('foreign_keys', true);

The following PRAGMAs are supported through the dedicated methods: quick_check, integrity_check, incremental_vacuum, foreign_key_check, foreign_key_list, index_info, index_list, index_xinfo, table_info, table_xinfo, and optimize. Other PRAGMAs can be executed directly via query().

Query Results

All queries return a [rows, metadata, query] tuple:

const [rows, meta, query] = await client.query<{
    column1: string,
    column2: number
}>('SELECT * FROM test WHERE column1 = ?', 'value');

// rows  - RecordType[] array of result rows
// meta  - { changedRows, affectedRows, insertId?, length }
// query - { query, values? } the executed query

Query parameters use ? placeholders across all drivers. The Postgres driver automatically converts these to $1, $2, ... numbered parameters internally.

Table Management

Creating Tables

await client.createTable('users', [
    { name: 'id', type: 'integer', nullable: false },
    { name: 'name', type: 'varchar(255)' },
    { name: 'email', type: 'varchar(255)', unique: true },
    { name: 'role', type: 'varchar(50)', default: 'user' },
    { name: 'score', type: 'float', nullable: true }
], ['id']); // primary key columns

Tables are created with IF NOT EXISTS. Columns marked unique: true automatically get a unique index.

Column Options

| Option | Type | Default | Description | |---|---|---|---| | name | string | required | Column name | | type | string | required | SQL type (e.g., varchar(255), integer, float) | | nullable | boolean | true | Whether the column allows NULL values | | default | string \| number \| boolean | — | Default value for the column | | unique | boolean | false | Creates a unique index on this column | | foreignKey | ForeignKey | — | Foreign key relationship (see below) |

Column types are validated against the pattern /^[a-zA-Z][a-zA-Z0-9 (),]*$/.

Foreign Key Constraints

await client.createTable('orders', [
    { name: 'id', type: 'integer' },
    {
        name: 'user_id',
        type: 'integer',
        foreignKey: {
            table: 'users',
            column: 'id',
            onDelete: Database.Table.ForeignKeyConstraint.CASCADE,
            onUpdate: Database.Table.ForeignKeyConstraint.CASCADE
        }
    }
], ['id']);

Available constraints: RESTRICT, CASCADE, NULL (SET NULL), DEFAULT (SET DEFAULT), NA (NO ACTION).

Indexes

// Standard index
await client.createIndex('users', ['email']);

// Unique index
await client.createIndex('users', ['email'], Database.Table.IndexType.UNIQUE);

Other Table Operations

// List all tables
const tables = await client.listTables();

// Drop tables
await client.dropTable('users');
await client.dropTable(['temp1', 'temp2']);

// Truncate tables
await client.truncate('users');

// Switch database (MySQL/MariaDB/Postgres)
await client.use('other_database');

Transactions

All drivers support transactions via transaction():

const results = await client.transaction([
    { query: 'INSERT INTO users (name) VALUES (?)', values: ['Alice'] },
    { query: 'INSERT INTO users (name) VALUES (?)', values: ['Bob'] }
]);

// results is an array of [rows, metadata, query] tuples, one per query

Set noError: true on individual queries to ignore their failures without aborting the transaction (MySQL/MariaDB/Postgres only — SQLite transactions are atomic and roll back entirely on any failure).

await client.transaction([
    { query: 'INSERT INTO users (name) VALUES (?)', values: ['Alice'] },
    { query: 'INSERT INTO users (name) VALUES (?)', values: ['duplicate'], noError: true },
    { query: 'INSERT INTO users (name) VALUES (?)', values: ['Charlie'] }
]);

Bulk Operations

Bulk Insert

await client.multiInsert('test', ['col1', 'col2'], [
    ['a', 1],
    ['b', 2],
    ['c', 3]
]);

Bulk Upsert

Insert rows or update them if they conflict on the primary key:

await client.multiUpdate('test', ['col1'], ['col1', 'col2'], [
    ['a', 10],  // updates existing row
    ['d', 40]   // inserts new row
]);

Both operations accept an optional useTransaction parameter (default: true) to control whether the bulk operation is wrapped in a transaction.

Prepared Queries

Generate query objects without executing them, useful for combining with other operations or inspecting the generated SQL:

const queries = client.prepareMultiInsert('test', ['col1', 'col2'], [
    ['a', 1],
    ['b', 2]
]);

const createQueries = client.prepareCreateTable('test', [
    { name: 'id', type: 'integer' }
], ['id']);

Connection Pool

MySQL/MariaDB use the mariadb driver's native connection pool. PostgreSQL uses pg's pool. SQLite uses a singleton instance with mutex-based concurrency.

Monitor pool status via getters:

console.log(client.idleConnections); // connections available in pool
console.log(client.totalConnections); // total pool size

Events

The Database class extends EventEmitter. Available events vary by driver:

MySQL / MariaDB:

| Event | Description | |---|---| | error | Connection error | | acquire | Connection acquired from pool | | connection | New connection created | | enqueue | Connection request queued (pool exhausted) | | release | Connection released back to pool |

Postgres:

| Event | Description | |---|---| | connect | New connection created | | acquire | Connection acquired from pool | | remove | Connection removed from pool | | error | Connection error |

client.on('error', (err) => {
    console.error('Database connection error:', err);
});

Utility Methods

// Escape a string value for safe SQL interpolation
const safe = client.escape(userInput);

// Escape an identifier (table/column name)
const id = client.escapeId('column name');

// Check the driver type
if (client.type === Database.Type.POSTGRES) { /* ... */ }
console.log(client.typeName); // 'MySQL', 'MariaDB', 'Postgres', or 'SQLite'

Escaping is driver-aware: Postgres uses pg-format, all others use sqlstring.

TLS / SSL

MySQL/MariaDB and Postgres disable TLS certificate validation by default (rejectUnauthorized: false) for development convenience. Set rejectUnauthorized: true in production:

// MySQL/MariaDB
const client = new MySQL({
    host: 'prod-host',
    useSSL: true,
    rejectUnauthorized: true
});

// Postgres
const client = new Postgres({
    host: 'prod-host',
    ssl: true,
    rejectUnauthorized: true
});

Environment Variables

createConnection() reads these environment variables when options are not provided directly:

| Variable | Description | Default | |---|---|---| | SQL_TYPE | Database type enum value (0=MySQL, 1=Postgres, 2=SQLite, 4=MariaDB) | 2 (SQLite) | | SQL_HOST | Database host | 127.0.0.1 | | SQL_PORT | Database port | Driver default | | SQL_USERNAME | Database user | '' | | SQL_PASSWORD | Database password | | | SQL_DATABASE | Database name | | | SQL_SSL | Enable SSL (true/false) | false | | SQL_FILENAME | SQLite database file path | :memory: |

A .env file is loaded automatically via dotenv.

Type Reference

// Database type enum
enum Database.Type {
    MYSQL = 0,
    POSTGRES = 1,
    SQLITE = 2,
    MARIADB = 4
}

// Query result tuple
type Database.Query.Result<T> = [T[], Database.Query.MetaData, Database.Query]

// Query metadata
type Database.Query.MetaData = {
    changedRows: number;
    affectedRows: number;
    insertId?: number;
    length: number;
}

// Query definition (for transactions and prepared queries)
type Database.Query = {
    query: string;
    values?: any[];
    noError?: boolean;
}

// Column definition
type Database.Table.Column = {
    name: string;
    type: string;
    nullable?: boolean;
    default?: string | number | boolean;
    unique?: boolean;
    foreignKey?: Database.Table.ForeignKey;
}

// Foreign key definition
type Database.Table.ForeignKey = {
    table: string;
    column: string;
    onUpdate?: Database.Table.ForeignKeyConstraint;
    onDelete?: Database.Table.ForeignKeyConstraint;
}

// Foreign key constraint actions
enum Database.Table.ForeignKeyConstraint {
    RESTRICT = 'RESTRICT',
    CASCADE = 'CASCADE',
    NULL = 'SET NULL',
    DEFAULT = 'SET DEFAULT',
    NA = 'NO ACTION'
}

// Index types
enum Database.Table.IndexType {
    NONE = '',
    UNIQUE = 'UNIQUE'
}

Known Limitations

  • Postgres ? placeholders: The library automatically converts ? placeholders to $1, $2, ... for Postgres. This conflicts with PostgreSQL JSON operators (?, ?|, ?&). Use the native $1, $2 syntax directly for queries involving JSON operators.

  • SQLite transaction atomicity: SQLite transactions use better-sqlite3's native transaction() callback, which is all-or-nothing. Individual query noError flags cannot be honored per-query — if any query fails, the entire transaction rolls back.

  • Column type validation: Column types in createTable are validated against the pattern /^[a-zA-Z][a-zA-Z0-9 (),]*$/. Types must start with a letter and can only contain letters, digits, spaces, parentheses, and commas.