This package provides 100% API compatibility with Node.js v24 built-in SQLite module (node:sqlite
). You can seamlessly switch between this package and the built-in module without changing any code.
// Using Node.js built-in SQLite (requires Node.js 22.5.0+ and --experimental-sqlite flag)
const { DatabaseSync } = require("node:sqlite");
// Using @photostructure/sqlite (works on Node.js 20+ without any flags)
const { DatabaseSync } = require("@photostructure/sqlite");
// The API is identical - no code changes needed!
Node.js has an experimental built-in SQLite module that provides synchronous database operations with excellent performance. However, it's only available in the newest Node.js versions, and requires the --experimental-sqlite
flag.
This package extracts that implementation into a standalone library that:
node:sqlite
- no code changes needednode:sqlite
becomes stable, switching back requires zero code changesnpm install @photostructure/sqlite
// If you have code using node:sqlite:
const { DatabaseSync } = require("node:sqlite");
// Simply replace with:
const { DatabaseSync } = require("@photostructure/sqlite");
// That's it! No other changes needed.
import { DatabaseSync } from "@photostructure/sqlite";
// Create an in-memory database
const db = new DatabaseSync(":memory:");
// Create a table
db.exec(`
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE
)
`);
// Insert data
const insertStmt = db.prepare("INSERT INTO users (name, email) VALUES (?, ?)");
const result = insertStmt.run("Alice Johnson", "alice@example.com");
console.log("Inserted user with ID:", result.lastInsertRowid);
// Query data
const selectStmt = db.prepare("SELECT * FROM users WHERE id = ?");
const user = selectStmt.get(result.lastInsertRowid);
console.log("User:", user);
// Clean up
db.close();
// Register a simple custom SQL function
db.function("multiply", (a, b) => a * b);
// With options
db.function(
"hash",
{
deterministic: true, // Same inputs always produce same output
directOnly: true, // Cannot be called from triggers/views
},
(value) => {
return crypto.createHash("sha256").update(String(value)).digest("hex");
},
);
// Aggregate function
db.aggregate("custom_sum", {
start: 0,
step: (sum, value) => sum + value,
result: (sum) => sum,
});
// Use in SQL
const result = db
.prepare("SELECT custom_sum(price) as total FROM products")
.get();
console.log(result.total);
// Simple backup
await db.backup("./backup.db");
// Backup with progress monitoring
await db.backup("./backup.db", {
rate: 10, // Copy 10 pages per iteration
progress: ({ totalPages, remainingPages }) => {
const percent = (
((totalPages - remainingPages) / totalPages) *
100
).toFixed(1);
console.log(`Backup progress: ${percent}%`);
},
});
// Backup specific attached database
db.exec("ATTACH DATABASE 'other.db' AS other");
await db.backup("./other-backup.db", {
source: "other", // Backup the attached database instead of main
});
// Restore from a backup file
import * as fs from "fs";
// Close the current database
db.close();
// Copy the backup file over the current database file
fs.copyFileSync("./backup.db", "./mydata.db");
// Reopen the database with the restored data
const restoredDb = new DatabaseSync("./mydata.db");
// Verify restoration
const count = restoredDb.prepare("SELECT COUNT(*) as count FROM users").get();
console.log(`Restored database has ${count.count} users`);
This package has full support for Node.js worker threads. Each worker thread gets its own isolated SQLite environment.
// main.js
const { Worker } = require("worker_threads");
// Spawn multiple workers to handle database operations
const worker1 = new Worker("./db-worker.js");
const worker2 = new Worker("./db-worker.js");
// Send queries to workers
worker1.postMessage({
sql: "SELECT * FROM users WHERE active = ?",
params: [true],
});
// db-worker.js
const { parentPort } = require("worker_threads");
const { DatabaseSync } = require("@photostructure/sqlite");
// Each worker creates its own database connection
const db = new DatabaseSync("./app.db");
parentPort.on("message", ({ sql, params }) => {
try {
const stmt = db.prepare(sql);
const results = stmt.all(...params);
stmt.finalize();
parentPort.postMessage({ success: true, results });
} catch (error) {
parentPort.postMessage({ success: false, error: error.message });
}
});
Key points:
DatabaseSync
instanceSQLite extensions can be loaded to add custom functionality. Extension loading requires explicit permission for security.
// Enable extension loading at database creation
const db = new DatabaseSync("./mydb.sqlite", {
allowExtension: true,
});
// Enable extension loading (required before loading)
db.enableLoadExtension(true);
// Load an extension
db.loadExtension("./extensions/vector.so");
// Optionally specify an entry point
db.loadExtension("./extensions/custom.so", "sqlite3_custom_init");
// Disable extension loading when done for security
db.enableLoadExtension(false);
SQLite's session extension allows you to record changes and apply them to other databases - perfect for synchronization, replication, or undo/redo functionality. This feature is available in both node:sqlite
and @photostructure/sqlite
, but not in better-sqlite3.
// Create a session to track changes
const session = db.createSession({ table: "users" });
// Make some changes
db.prepare("UPDATE users SET name = ? WHERE id = ?").run("Alice Smith", 1);
db.prepare("INSERT INTO users (name, email) VALUES (?, ?)").run(
"Bob",
"bob@example.com",
);
// Get the changes as a changeset
const changeset = session.changeset();
session.close();
// Apply changes to another database
const otherDb = new DatabaseSync("./replica.db");
const applied = otherDb.applyChangeset(changeset, {
onConflict: (conflict) => {
console.log(`Conflict on table ${conflict.table}`);
return constants.SQLITE_CHANGESET_REPLACE; // Resolve by replacing
},
});
const stmt = db.prepare("SELECT * FROM users WHERE name = ? AND age > ?");
// Positional parameters
const users1 = stmt.all("Alice", 25);
// Named parameters (with object)
const stmt2 = db.prepare(
"SELECT * FROM users WHERE name = $name AND age > $age",
);
const users2 = stmt2.all({ name: "Alice", age: 25 });
interface User {
id: number;
name: string;
email: string;
created_at: string;
}
const db = new DatabaseSync("users.db");
const stmt = db.prepare("SELECT * FROM users WHERE id = ?");
const user = stmt.get(1) as User;
console.log(`User: ${user.name} <${user.email}>`);
This package provides performance comparable to Node.js's built-in SQLite and better-sqlite3, with:
Performance is quite similar to node:sqlite and better-sqlite3, while significantly faster than async sqlite3 due to synchronous operations.
Platform | x64 | ARM64 | Notes |
---|---|---|---|
Linux | ✅ | ✅ | GLIBC 2.31+ (Ubuntu 20.04+, Debian 11+, RHEL 8+) |
Alpine | ✅ | ✅ | Alpine 3.21+ (musl libc) |
macOS | ✅ | ✅ | macOS 10.15+ |
Windows | ✅ | ✅ | Windows 10+ |
Supported distributions (with prebuilt binaries):
Not supported (GLIBC too old):
Note: While Node.js 20 itself supports these older distributions, our prebuilt binaries require GLIBC 2.31+ due to toolchain requirements. Users on older distributions can still compile from source if they have a compatible compiler (GCC 10+ with C++20 support).
Prebuilt binaries are provided for all supported platforms. If a prebuilt binary isn't available, the package will compile from source using node-gyp.
build-essential
, python3
(3.8+), GCC 10+ or Clang 10+When choosing a SQLite library for Node.js, you have several excellent options. Here's how @photostructure/sqlite
compares to the alternatives:
node:sqlite
— Node.js Built-in ModuleThe official SQLite module included with Node.js 22.5.0+ (experimental)
✨ Pros:
⚠️ Cons:
--experimental-sqlite
to enable🎯 Best for: Experimental projects, early adopters, and preparing for the future when it becomes stable.
better-sqlite3
— The Performance ChampionThe most popular high-performance synchronous SQLite library
✨ Pros:
⚠️ Cons:
🎯 Best for: High-performance applications where you want maximum speed and control over the API.
sqlite3
— The Async ClassicThe original asynchronous SQLite binding for Node.js
✨ Pros:
⚠️ Cons:
🎯 Best for: Legacy codebases, apps requiring true async operations, or when you need SQLCipher encryption.
@photostructure/sqlite
when you want:node:sqlite
node:sqlite
becomes stablebetter-sqlite3
when you want:sqlite3
when you have:node:sqlite
when you're:Contributions are welcome! This project maintains 100% API compatibility with Node.js's built-in SQLite module. Please run tests with npm test
and ensure code passes linting with npm run lint
before submitting changes. When adding new features, include corresponding tests and ensure they match Node.js SQLite behavior exactly.
The project includes automated sync scripts to keep up-to-date with:
npm run sync:node
npm run sync:sqlite
This project takes security seriously and employs multiple layers of protection:
# Install security tools (OSV Scanner, etc.)
./scripts/setup-security-tools.sh
# Run all security scans
npm run security
For details, see our Security Policy. To report vulnerabilities, please email security@photostructure.com.
MIT License - see LICENSE for details.
This package includes SQLite, which is in the public domain, as well as code from the Node.js project, which is MIT licensed.
✅ Fully Supported:
All features of Node.js's built-in SQLite module are now fully implemented!
For concurrent access within the same process, multiple database connections work well with WAL mode enabled.
This project was built with substantial assistance from Claude Code, an AI coding assistant.
Note that all changes are human-reviewed before merging.
This project demonstrates how AI-assisted development can accelerate complex system programming while maintaining high code quality through comprehensive testing and human oversight.
Note: This package is not affiliated with the Node.js project. It extracts and redistributes Node.js's SQLite implementation under the MIT license.