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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@arcmantle/sqlite-wrapper

v1.0.3

Published

A modern, type-safe SQLite wrapper built on better-sqlite3 with fluent API, automatic resource management, and comprehensive TypeScript support

Readme

@arcmantle/sqlite-wrapper

A modern, type-safe SQLite wrapper built on top of better-sqlite3 that provides a fluent, chainable API for database operations. This library offers automatic resource management, WAL mode by default, and comprehensive TypeScript support.

Features

  • Type-Safe Query Building: Full TypeScript support with type inference for table schemas
  • Fluent API: Chainable method calls for building complex queries
  • Automatic Resource Management: Built-in support for JavaScript's using keyword and Symbol.dispose
  • WAL Mode: Write-Ahead Logging enabled by default for better concurrency
  • Transaction Support: Simple transaction management with automatic rollback on errors
  • Schema Management: Built-in table creation and column manipulation utilities
  • Safe String Handling: Automatic SQL injection prevention through proper escaping
  • Filter Builder: Comprehensive filtering system with logical operators and pattern matching

Installation

# npm
npm install @arcmantle/sqlite-wrapper

# yarn
yarn add @arcmantle/sqlite-wrapper

# pnpm
pnpm add @arcmantle/sqlite-wrapper

Quick Start

Basic Usage

import { Query } from '@arcmantle/sqlite-wrapper';

// Create a new query instance
using query = new Query('database.db');

// Create a table
query.define('users')
  .primaryKey('id')
  .column('name', 'TEXT')
  .column('email', 'TEXT', { nullable: false })
  .column('age', 'INTEGER', { value: 0 })
  .query();

// Insert data
query.insert('users')
  .values({ name: 'John Doe', email: '[email protected]', age: 30 })
  .query();

// Query data
const users = query.from('users')
  .select('name', 'email')
  .where(filter => filter.eq('age', 30))
  .query();

Type-Safe Queries

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

using query = new Query('database.db');

// Type-safe operations
const adults = query.from<User>('users')
  .select('name', 'email')  // TypeScript will validate these field names
  .where(filter => filter
    .and(
      filter.exists('email'),
      filter.oneOf('age', 18, 21, 25)
    )
  )
  .orderBy('name', 'asc')
  .limit(10)
  .query();

API Reference

Query Class

The main entry point for database operations.

Constructor

  • new Query(filename?: string) - Creates a new query instance with optional database file path

Methods

  • from<T>(table: string): SelectBuilder<T> - Creates a SELECT query builder
  • insert<T>(table: string): InsertBuilder<T> - Creates an INSERT query builder
  • update<T>(table: string): UpdateBuilder<T> - Creates an UPDATE query builder
  • delete<T>(table: string): DeleteBuilder<T> - Creates a DELETE query builder
  • define<T>(table: string): DefineBuilder<T> - Creates a table definition builder
  • transaction(fn: (query: Query) => void): void - Executes operations in a transaction

SELECT Queries

query.from('users')
  .select('name', 'email')           // Specify columns
  .where(filter => filter.eq('active', true))  // Add conditions
  .groupBy('department')             // Group results
  .orderBy('name', 'asc', true)      // Sort with nulls last
  .limit(50)                         // Limit results
  .offset(10)                        // Skip rows
  .query();                          // Execute query

INSERT Queries

query.insert('users')
  .values({
    name: 'Jane Smith',
    email: '[email protected]',
    age: 28
  })
  .query();

UPDATE Queries

query.update('users')
  .values({ age: 29 })
  .where(filter => filter.eq('id', 1))
  .query();

DELETE Queries

query.delete('users')
  .where(filter => filter.eq('active', false))
  .query();

Table Creation

query.define('products')
  .primaryKey('id')
  .column('name', 'TEXT', { nullable: false })
  .column('price', 'REAL', { value: 0.0 })
  .column('description', 'TEXT', { nullable: true })
  .column('in_stock', 'INTEGER', { value: true })
  .query();

Filter Conditions

The filter system provides comprehensive condition building:

query.from('users')
  .where(filter => filter
    .and(
      filter.eq('active', true),           // Equality
      filter.startsWith('name', 'John'),   // String prefix
      filter.contains('email', '@gmail'),  // String contains
      filter.oneOf('age', 25, 30, 35),     // Value in list
      filter.exists('phone'),              // Not null
      filter.glob('name', 'J*n')           // Pattern matching
    )
  )
  .query();

Available filter methods:

  • eq(field, value) - Equality comparison
  • startsWith(field, value) - String starts with
  • endsWith(field, value) - String ends with
  • contains(field, value) - String contains
  • oneOf(field, ...values) - Value in list
  • notOneOf(field, ...values) - Value not in list
  • exists(field) - Field is not null
  • notExists(field) - Field is null
  • glob(field, pattern) - Unix shell-style pattern matching
  • and(...conditions) - Logical AND
  • or(...conditions) - Logical OR

Utility Functions

Table Management

import { tableExists, dropColumn, getCreateQuery, getTableColumns } from '@arcmantle/sqlite-wrapper';

// Check if table exists
if (tableExists('users')) {
  console.log('Users table exists');
}

// Get table creation SQL
const createSQL = getCreateQuery(db, 'users');

// Get column information
const columns = getTableColumns(db, 'users');

// Drop a column (recreates table)
dropColumn(db, 'users', 'old_column');

String Utilities

import { sql, escapeString } from '@arcmantle/sqlite-wrapper';

// Template literal for raw SQL
const rawQuery = sql`SELECT * FROM users WHERE id = ${userId}`;

// Escape strings for SQL
const safeString = escapeString("Don't break SQL");

Type System

Branded Types

The library uses branded types to provide additional type safety:

import type { Branded } from '@arcmantle/sqlite-wrapper';

type UserId = Branded<number, 'UserId'>;

Optional Properties

Utility type for making specific properties optional:

import type { Optional } from '@arcmantle/sqlite-wrapper';

type CreateUser = Optional<User, 'id'>;  // Makes 'id' optional

Data Models

Extend the DataModel class for automatic property assignment:

import { DataModel } from '@arcmantle/sqlite-wrapper';

class User extends DataModel {
  id!: number;
  name!: string;
  email!: string;

  constructor(values: any) {
    super(values);  // Automatically assigns all properties
  }

  static parse(data: unknown): User {
    // Custom parsing logic
    return new User(data);
  }
}

Resource Management

The library supports automatic resource cleanup:

// Using 'using' keyword (ES2023)
using query = new Query('database.db');
// Database connection automatically closed when scope ends

// Manual disposal
const query = new Query('database.db');
query[Symbol.dispose]();  // Manually close connection

Transactions

Execute multiple operations atomically:

using query = new Query('database.db');

query.transaction(tx => {
  tx.insert('users').values({ name: 'John' }).query();
  tx.insert('profiles').values({ userId: 1 }).query();
  // All operations committed together, or all rolled back on error
});

Advanced Features

Custom SQL

For complex queries, you can access the underlying prepared statements:

const builder = query.from('users').select('*');
const sqlString = builder.queryAsString;  // Get the SQL string
console.log(sqlString);  // Useful for debugging

Error Handling

All query methods include built-in error handling:

// SELECT queries return empty arrays on error
const users = query.from('users').query();  // [] if error occurs

// INSERT/UPDATE/DELETE return undefined on error
const result = query.insert('users').values(data).query();
if (result) {
  console.log(`Inserted row with ID: ${result.lastInsertRowid}`);
}

Requirements

  • Node.js 16+
  • TypeScript 4.5+ (for branded types)
  • ES2023+ (for using keyword support)

License

This project is licensed under the Apache License 2.0.

Contributing

This library is designed to be a lightweight, type-safe wrapper around better-sqlite3. When contributing:

  1. Maintain TypeScript type safety
  2. Keep the fluent API consistent
  3. Add appropriate JSDoc comments
  4. Test with various SQLite scenarios
  5. Follow the existing code style

Related Packages