Go to file
2026-03-02 14:59:54 +01:00
dist Resolve typescript errors 2026-03-02 14:58:14 +01:00
src Resolve typescript errors 2026-03-02 14:58:14 +01:00
.gitignore Updates 2026-03-02 10:44:30 +01:00
.npmrc Updates 2026-03-02 10:44:30 +01:00
bun.lock Add Backup and Restore 2026-03-02 14:51:19 +01:00
CLAUDE.md first Commit 2026-03-02 07:56:05 +01:00
package.json Update README 2026-03-02 14:59:54 +01:00
publish.sh Add Engine V1 2026-03-02 10:24:40 +01:00
README.md Update README 2026-03-02 14:59:54 +01:00
tsconfig.json Updates 2026-03-02 10:44:30 +01:00

Bun SQLite

@moduletrace/bun-sqlite

A schema-driven SQLite manager for Bun, featuring automatic schema synchronization, type-safe CRUD operations, vector embedding support (via sqlite-vec), and TypeScript type definition generation.


Table of Contents


Features

  • Schema-first design — define your database in TypeScript; the library syncs your SQLite file to match
  • Automatic migrations — adds new columns, recreates tables for complex changes, drops removed tables
  • Type-safe CRUD — fully generic select, insert, update, delete functions with TypeScript generics
  • Rich query DSL — filtering, ordering, pagination, joins, grouping, full-text search, sub-query counts
  • Vector table support — create and manage sqlite-vec virtual tables for AI/ML embeddings
  • TypeScript codegen — generate .ts type definitions from your schema automatically
  • Zero-config defaultsid, created_at, and updated_at fields are added to every table automatically

Prerequisites

@moduletrace/bun-sqlite is published to a private Gitea npm registry. You must configure your package manager to resolve the @moduletrace scope from that registry before installing.

Add the following to your project's .npmrc file (create it at the root of your project if it doesn't exist):

@moduletrace:registry=https://git.tben.me/api/packages/moduletrace/npm/

This works for both bun and npm.


Installation

bun add @moduletrace/bun-sqlite

Quick Start

1. Create the config file

Create bun-sqlite.config.ts at your project root:

import type { BunSQLiteConfig } from "@moduletrace/bun-sqlite";

const config: BunSQLiteConfig = {
    db_name: "my-app.db",
    db_schema_file_name: "schema.ts",
    db_dir: "./db", // optional: where to store the db file
    db_backup_dir: ".backups", // optional: name of backups directory. Relative to the db dir.
    typedef_file_path: "./db/types/db.ts", // optional: where to write generated types
};

export default config;

2. Define your schema

Create ./db/schema.ts (matching db_schema_file_name above):

import type { BUN_SQLITE_DatabaseSchemaType } from "@moduletrace/bun-sqlite";

const schema: BUN_SQLITE_DatabaseSchemaType = {
    dbName: "my-app",
    tables: [
        {
            tableName: "users",
            fields: [
                { fieldName: "first_name", dataType: "TEXT" },
                { fieldName: "last_name", dataType: "TEXT" },
                { fieldName: "email", dataType: "TEXT", unique: true },
            ],
        },
    ],
};

export default schema;

3. Sync the schema to SQLite

bunx bun-sqlite schema

This creates the SQLite database file and creates/updates all tables to match your schema.

4. Use the CRUD API

import BunSQLite from "@moduletrace/bun-sqlite";

// Insert
await BunSQLite.insert({
    table: "users",
    data: [{ first_name: "Alice", email: "alice@example.com" }],
});

// Select
const result = await BunSQLite.select({ table: "users" });
console.log(result.payload); // Alice's row

// Update
await BunSQLite.update({
    table: "users",
    targetId: 1,
    data: { first_name: "Alicia" },
});

// Delete
await BunSQLite.delete({ table: "users", targetId: 1 });

Configuration

The config file must be named bun-sqlite.config.ts and placed at the root of your project.

Field Type Required Description
db_name string Yes Filename for the SQLite database (e.g. "app.db")
db_schema_file_name string Yes Filename of the schema file relative to db_dir (or root if db_dir is not set)
db_backup_dir string No Directory for database backups, relative to db_dir
db_dir string No Root directory for the database file and schema. Defaults to project root
typedef_file_path string No Output path for generated TypeScript types, relative to project root
max_backups number No Maximum number of backup files to keep. Oldest are deleted automatically. Defaults to 10

Schema Definition

Database Schema

interface BUN_SQLITE_DatabaseSchemaType {
    dbName?: string;
    tables: BUN_SQLITE_TableSchemaType[];
}

Table Schema

interface BUN_SQLITE_TableSchemaType {
    tableName: string;
    tableDescription?: string;
    fields: BUN_SQLITE_FieldSchemaType[];
    indexes?: BUN_SQLITE_IndexSchemaType[];
    uniqueConstraints?: BUN_SQLITE_UniqueConstraintSchemaType[];
    parentTableName?: string; // inherit fields from another table in the schema
    tableNameOld?: string; // rename: set this to the old name to trigger ALTER TABLE RENAME
    isVector?: boolean; // mark this as a sqlite-vec virtual table
    vectorType?: string; // virtual table type, defaults to "vec0"
}

Field Schema

type BUN_SQLITE_FieldSchemaType = {
    fieldName?: string;
    dataType: "TEXT" | "INTEGER";
    primaryKey?: boolean;
    autoIncrement?: boolean;
    notNullValue?: boolean;
    unique?: boolean;
    defaultValue?: string | number;
    defaultValueLiteral?: string; // raw SQL literal, e.g. "CURRENT_TIMESTAMP"
    foreignKey?: BUN_SQLITE_ForeignKeyType;
    isVector?: boolean; // vector embedding column
    vectorSize?: number; // embedding dimensions (default: 1536)
    sideCar?: boolean; // sqlite-vec "+" prefix for side-car columns
    updatedField?: boolean; // flag that this field definition has changed
};

Foreign Key

interface BUN_SQLITE_ForeignKeyType {
    destinationTableName: string;
    destinationTableColumnName: string;
    cascadeDelete?: boolean;
    cascadeUpdate?: boolean;
}

Index

interface BUN_SQLITE_IndexSchemaType {
    indexName?: string;
    indexType?: "regular" | "full_text" | "vector";
    indexTableFields?: { value: string; dataType: string }[];
}

Unique Constraint

interface BUN_SQLITE_UniqueConstraintSchemaType {
    constraintName?: string;
    constraintTableFields?: { value: string }[];
}

CLI Commands

The package provides a bun-sqlite CLI binary.

schema — Sync database to schema

bunx bun-sqlite schema [options]
Option Description
-v, --vector Drop and recreate all vector (sqlite-vec) virtual tables
-t, --typedef Also generate TypeScript type definitions after syncing

Examples:

# Sync schema only
bunx bun-sqlite schema

# Sync schema and regenerate types
bunx bun-sqlite schema --typedef

# Sync schema, recreate vector tables, and regenerate types
bunx bun-sqlite schema --vector --typedef

typedef — Generate TypeScript types only

bunx bun-sqlite typedef

Reads the schema and writes TypeScript type definitions to the path configured in typedef_file_path.


backup — Back up the database

bunx bun-sqlite backup

Copies the current database file into db_backup_dir with a timestamped filename. After copying, the oldest backups are automatically pruned so the number of stored backups never exceeds max_backups (default: 10).

Example:

bunx bun-sqlite backup
# Backing up database ...
# DB Backup Success!

restore — Restore the database from a backup

bunx bun-sqlite restore

Presents an interactive list of available backups sorted by date (newest first). Select a backup to overwrite the current database file with it.

Example:

bunx bun-sqlite restore
# Restoring up database ...
# ? Select a backup: (Use arrow keys)
#  Backup #1: Mon Mar 02 2026 14:30:00
#   Backup #2: Sun Mar 01 2026 09:15:42
# DB Restore Success!

If no backups exist, the command exits with an error and a reminder to run backup first.


CRUD API

Import the default export:

import BunSQLite from "@moduletrace/bun-sqlite";

All methods return an APIResponseObject<T>:

{
    success: boolean;
    payload?: T[];           // array of rows (select)
    singleRes?: T;           // first row (select)
    count?: number;          // total count (when count: true)
    postInsertReturn?: {
        affectedRows?: number;
        insertId?: number;
    };
    error?: string;
    msg?: string;
    debug?: any;
}

Select

BunSQLite.select<T>({ table, query?, count?, targetId? })
Parameter Type Description
table string Table name
query ServerQueryParam<T> Query/filter options (see Query API)
count boolean Return row count instead of rows
targetId string | number Shorthand to filter by id

Examples:

// Get all users
const res = await BunSQLite.select({ table: "users" });

// Get by ID
const res = await BunSQLite.select({ table: "users", targetId: 42 });

// Filter with LIKE
const res = await BunSQLite.select<UserType>({
    table: "users",
    query: {
        query: {
            first_name: { value: "Ali", equality: "LIKE" },
        },
    },
});

// Count rows
const res = await BunSQLite.select({ table: "users", count: true });
console.log(res.count);

// Pagination
const res = await BunSQLite.select({
    table: "users",
    query: { limit: 10, page: 2 },
});

Insert

BunSQLite.insert<T>({ table, data });
Parameter Type Description
table string Table name
data T[] Array of row objects to insert

created_at and updated_at are set automatically to Date.now().

Example:

const res = await BunSQLite.insert({
    table: "users",
    data: [
        { first_name: "Alice", last_name: "Smith", email: "alice@example.com" },
        { first_name: "Bob", last_name: "Jones", email: "bob@example.com" },
    ],
});

console.log(res.postInsertReturn?.insertId); // last inserted row ID

Update

BunSQLite.update<T>({ table, data, query?, targetId? })
Parameter Type Description
table string Table name
data Partial<T> Fields to update
query ServerQueryParam<T> WHERE clause conditions
targetId string | number Shorthand to filter by id

A WHERE clause is required. If no condition matches, success is false.

updated_at is set automatically to Date.now().

Examples:

// Update by ID
await BunSQLite.update({
    table: "users",
    targetId: 1,
    data: { first_name: "Alicia" },
});

// Update with custom query
await BunSQLite.update({
    table: "users",
    data: { last_name: "Doe" },
    query: {
        query: {
            email: { value: "alice@example.com", equality: "EQUAL" },
        },
    },
});

Delete

BunSQLite.delete<T>({ table, query?, targetId? })
Parameter Type Description
table string Table name
query ServerQueryParam<T> WHERE clause conditions
targetId string | number Shorthand to filter by id

A WHERE clause is required. If no condition is provided, success is false.

Examples:

// Delete by ID
await BunSQLite.delete({ table: "users", targetId: 1 });

// Delete with condition
await BunSQLite.delete({
    table: "users",
    query: {
        query: {
            first_name: { value: "Ben", equality: "LIKE" },
        },
    },
});

Raw SQL

BunSQLite.sql<T>({ sql, values? })
Parameter Type Description
sql string Raw SQL statement
values (string | number)[] Parameterized values

SELECT statements return rows; all other statements return postInsertReturn.

Examples:

// SELECT
const res = await BunSQLite.sql<UserType>({ sql: "SELECT * FROM users" });
console.log(res.payload);

// INSERT with params
await BunSQLite.sql({
    sql: "INSERT INTO users (first_name, email) VALUES (?, ?)",
    values: ["Charlie", "charlie@example.com"],
});

Query API Reference

The query parameter on select, update, and delete accepts a ServerQueryParam<T> object:

type ServerQueryParam<T> = {
    query?: { [key in keyof T]: ServerQueryObject };
    selectFields?: (keyof T | { fieldName: keyof T; alias?: string })[];
    omitFields?: (keyof T)[];
    limit?: number;
    page?: number;
    offset?: number;
    order?:
        | { field: keyof T; strategy: "ASC" | "DESC" }
        | { field: keyof T; strategy: "ASC" | "DESC" }[];
    searchOperator?: "AND" | "OR"; // how multiple query fields are joined (default: AND)
    join?: ServerQueryParamsJoin[];
    group?:
        | keyof T
        | { field: keyof T; table?: string }
        | (keyof T | { field: keyof T; table?: string })[];
    countSubQueries?: ServerQueryParamsCount[];
    fullTextSearch?: {
        fields: (keyof T)[];
        searchTerm: string;
        scoreAlias: string;
    };
};

Equality Operators

Set equality on any query field to control the comparison:

Equality SQL Equivalent
EQUAL (default) =
NOT EQUAL !=
LIKE LIKE '%value%'
LIKE_RAW LIKE 'value' (no auto-wrapping)
LIKE_LOWER LOWER(field) LIKE '%value%'
NOT LIKE NOT LIKE '%value%'
GREATER THAN >
GREATER THAN OR EQUAL >=
LESS THAN <
LESS THAN OR EQUAL <=
IN IN (val1, val2, ...) — pass array as value
NOT IN NOT IN (...)
BETWEEN BETWEEN val1 AND val2 — pass [val1, val2] as value
IS NULL IS NULL
IS NOT NULL IS NOT NULL
MATCH sqlite-vec vector nearest-neighbor search

Example:

// Find users with email NOT NULL, ordered by created_at DESC, limit 20
const res = await BunSQLite.select<UserType>({
    table: "users",
    query: {
        query: {
            email: { equality: "IS NOT NULL" },
        },
        order: { field: "created_at", strategy: "DESC" },
        limit: 20,
    },
});

JOIN

const res = await BunSQLite.select({
    table: "posts",
    query: {
        join: [
            {
                joinType: "LEFT JOIN",
                tableName: "users",
                match: { source: "user_id", target: "id" },
                selectFields: ["first_name", "email"],
            },
        ],
    },
});

Vector Table Support

@moduletrace/bun-sqlite integrates with sqlite-vec for storing and querying vector embeddings.

Define a vector table in the schema

{
    tableName: "documents",
    isVector: true,
    vectorType: "vec0",   // defaults to "vec0"
    fields: [
        {
            fieldName: "embedding",
            dataType: "TEXT",
            isVector: true,
            vectorSize: 1536,   // embedding dimensions
        },
        {
            fieldName: "title",
            dataType: "TEXT",
            sideCar: true,  // stored as a side-car column (+title) for efficiency
        },
        {
            fieldName: "body",
            dataType: "TEXT",
            sideCar: true,
        },
    ],
}

Side-car columns (sideCar: true) use sqlite-vec's +column syntax. They are stored separately from the vector index, keeping the index lean and fast while still being queryable alongside vector results.

Sync vector tables

# Initial sync
bunx bun-sqlite schema

# Recreate vector tables (e.g. after changing vectorSize)
bunx bun-sqlite schema --vector

Query vectors

const res = await BunSQLite.select({
    table: "documents",
    query: {
        query: {
            embedding: {
                equality: "MATCH",
                value: "<serialized-vector>",
                vector: true,
                vectorFunction: "vec_f32",
            },
        },
        limit: 5,
    },
});

TypeScript Type Generation

Run the typedef command (or pass --typedef to schema) to generate a .ts file containing:

  • A const array of all table names (BunSQLiteTables)
  • A type for each table (named BUN_SQLITE_<DB_NAME>_<TABLE_NAME>)
  • A union type BUN_SQLITE_<DB_NAME>_ALL_TYPEDEFS

Example output (db/types/db.ts):

export const BunSQLiteTables = ["users"] as const;

export type BUN_SQLITE_MY_APP_USERS = {
    /** The unique identifier of the record. */
    id?: number;
    /** The time when the record was created. (Unix Timestamp) */
    created_at?: number;
    /** The time when the record was updated. (Unix Timestamp) */
    updated_at?: number;
    first_name?: string;
    last_name?: string;
    email?: string;
};

export type BUN_SQLITE_MY_APP_ALL_TYPEDEFS = BUN_SQLITE_MY_APP_USERS;

Use the generated types with the CRUD API for full type safety:

import BunSQLite from "@moduletrace/bun-sqlite";
import { BUN_SQLITE_MY_APP_USERS, BunSQLiteTables } from "./db/types/db";

const res = await BunSQLite.select<BUN_SQLITE_MY_APP_USERS>({
    table: "users" as (typeof BunSQLiteTables)[number],
});

Default Fields

Every table automatically receives the following fields — you do not need to declare them in your schema:

Field Type Description
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL Unique row identifier
created_at INTEGER Unix timestamp set on insert
updated_at INTEGER Unix timestamp updated on every update

Project Structure

bun-sqlite/
├── src/
│   ├── index.ts                        # Main export (BunSQLite object)
│   ├── commands/
│   │   ├── index.ts                    # CLI entry point
│   │   ├── schema.ts                   # `schema` command
│   │   ├── typedef.ts                  # `typedef` command
│   │   ├── backup.ts                   # `backup` command
│   │   └── restore.ts                  # `restore` command
│   ├── functions/
│   │   └── init.ts                     # Config + schema loader
│   ├── lib/sqlite/
│   │   ├── index.ts                    # Database client (bun:sqlite + sqlite-vec)
│   │   ├── db-schema-manager.ts        # Schema synchronization engine
│   │   ├── db-select.ts                # Select implementation
│   │   ├── db-insert.ts                # Insert implementation
│   │   ├── db-update.ts                # Update implementation
│   │   ├── db-delete.ts                # Delete implementation
│   │   ├── db-sql.ts                   # Raw SQL implementation
│   │   ├── db-generate-type-defs.ts    # Type def generator
│   │   └── schema-to-typedef.ts        # Schema-to-TypeScript converter
│   ├── types/
│   │   └── index.ts                    # All TypeScript types and interfaces
│   └── utils/
│       ├── sql-generator.ts            # SELECT query builder
│       ├── sql-insert-generator.ts     # INSERT query builder
│       ├── sql-gen-operator-gen.ts     # Equality operator mapper
│       ├── sql-equality-parser.ts      # Equality string parser
│       ├── append-default-fields-to-db-schema.ts
│       ├── grab-db-dir.ts              # Resolve db/backup directory paths
│       ├── grab-db-backup-file-name.ts # Generate timestamped backup filename
│       ├── grab-sorted-backups.ts      # List backups sorted newest-first
│       ├── grab-backup-data.ts         # Parse metadata from a backup filename
│       └── trim-backups.ts             # Prune oldest backups over max_backups
└── test/
    └── test-01/                        # Example project using the library
        ├── bun-sqlite.config.ts
        ├── db/
        │   ├── bun-sqlite-schema.ts
        │   └── types/bun-sqlite.ts     # Generated types
        └── src/
            ├── insert.ts
            ├── select.ts
            ├── delete.ts
            └── sql.ts

License

MIT