node-sqlite
A zero-dependency TypeScript wrapper that adds Promises and SQL-based migrations to Node.js sqlite3.
Repository Health
Technical Analysis
sqlite is a lightweight TypeScript wrapper around the classic sqlite3 Node.js driver that replaces its callback-based API with native ES6 Promises, while staying compatible with any driver conforming to the same interface (including cached and offline variants). It layers on generics for typed query results, tagged-template SQL support via sql-template-strings, and a small file-based migration runner that tracks applied migrations in the database itself.
Because it has effectively zero runtime dependencies of its own and simply wraps whatever sqlite3-compatible driver is passed in, teams often reach for it in Node.js services, CLIs, and Electron apps that need a straightforward, promise-friendly SQLite layer without adopting a full ORM.
What You Get
- Promise-wrapped Database class with run/get/all/each/exec/prepare methods mirroring the underlying sqlite3 API
- Statement class for prepared-statement reuse with bind/get/all/each/reset/finalize
- Built-in SQL-file migration runner (db.migrate()) that tracks applied/rolled-back migrations in a database table
- Full TypeScript typings, including generics for typed row results and driver-specific typings via ISqlite/IMigrate namespaces
- Support for sql-template-strings tagged templates as an alternative to positional/named parameter binding
Common Use Cases
- Adding a lightweight embedded database to a CLI tool or Electron desktop app without pulling in a full ORM
- Running SQL-based schema migrations in a Node.js service using the built-in migrate() API
- Prototyping a Node.js backend against SQLite before migrating to Postgres/MySQL with minimal API changes
- Wrapping a custom or offline sqlite3-compatible driver (e.g. sqlite3-offline-next) behind the same promise API
Under The Hood
Architecture
The library is organized around three cooperating pieces: Database (src/Database.ts) wraps a sqlite3-compatible driver instance and exposes promise-returning run/get/all/each/exec/prepare/loadExtension methods, each implemented by wrapping the driver’s callback signature in a new Promise(...); Statement (src/Statement.ts) mirrors the same pattern for prepared statements, adding bind/reset/finalize; and a small interfaces.ts namespace pair (ISqlite/IMigrate) centralizes the shared TypeScript contracts both classes and the migration runner depend on. Migrations live in a separate module (src/utils/migrate.ts) that stays decoupled from the Database class beyond calling its run/all/exec methods, reading numbered NNN-name.sql files from a configurable directory, tracking applied state in a migrations table, and running each migration inside an explicit BEGIN/COMMIT/ROLLBACK block. This composition keeps the driver wrapper, statement wrapper, and migration logic independently testable, though very little abstraction protects callers from the underlying sqlite3 driver’s own behavior — the design intentionally stays a thin promise layer rather than a full ORM.
Tech Stack
The project is TypeScript-first (tsconfig.json, the large majority of the codebase per GitHub’s language breakdown) compiled with tsc to both CommonJS (build/index.js) and an ESM entry (src/index.mjs) declared via package.json exports. Its only real runtime contract is the sqlite3 package (or any API-compatible driver, including sqlite3.cached.Database or third-party drivers like sqlite3-offline-next), which is a dev/peer dependency rather than a hard runtime one — the library ships with effectively zero production dependencies of its own. Testing runs on Jest with ts-jest, linting uses standard/eslint with the TypeScript parser, documentation is generated via typedoc + typedoc-plugin-markdown into the checked-in docs/ directory, and CI runs through CircleCI. Release tooling (@theo.gravity/version-bump, @theo.gravity/changelog-version) automates changelog and version bumps.
Code Quality
Tests live under src/tests/ (index, each, and Sqlite3-specific suites) and src/utils/tests/ (migrate, format-error), each exercising the real in-memory SQLite driver rather than mocks — for example the migration tests run actual .sql files against :memory: databases with both cached and uncached drivers. Error handling is centralized through formatError() (src/utils/format-error.ts), which normalizes whatever the underlying driver throws into a proper Error instance so callers get consistent stack traces. Naming is conventional and methods are documented with JSDoc blocks linking back to the underlying sqlite3 wiki pages. Type safety is reasonable but not airtight: several methods (on, configure, generic each/all overloads) fall back to any where the driver’s dynamic callback signatures make stricter typing impractical, and two methods (serialize/parallelize) are explicitly left unimplemented and throw at runtime rather than being typed as unavailable.
API Design
For a project that could have simply been “sqlite3 with promises,” the API adds a few deliberate ergonomics: generic type parameters on get<T>/all<T>/prepare let callers get typed rows without a schema-generation step, sql-template-strings compatibility gives an escape hatch from manual parameter binding, and db.migrate() turns ad-hoc schema setup into a first-class, file-based, rollback-aware operation — a feature most thin driver wrappers skip entirely. The tradeoff is surface-level: the API remains a near 1:1 mirror of the callback-era sqlite3 methods (down to reproducing its multiple-overload each() signature), optimizing for familiarity to existing sqlite3 users over inventing a new query-building abstraction.
Used by 2 apps in this directory
Continue
Developer Tools · AI Development · AI Code Assistants
Open-source coding agent for VS Code, JetBrains, and CLI with support for 30+ LLM providers.
evidence
Analytics · Data Engineering
Turn SQL queries and markdown files into polished, interactive data apps and business intelligence reports — no drag-and-drop, no GUI, just code.