better-sqlite3 is the fastest SQLite driver for Node.js. It's synchronous (no callbacks, no promises — just function calls) and compiles a native C addon at install time. That native compilation is the source of most setup issues.
Install
npm install better-sqlite3
This downloads prebuilt binaries for most platforms. If no prebuild is available, it compiles from source — which requires a C toolchain.
If Installation Fails
Missing build tools on Linux:
sudo apt install build-essential python3
npm install better-sqlite3
Missing build tools on macOS:
xcode-select --install
npm install better-sqlite3
Windows:
npm install --global windows-build-tools
npm install better-sqlite3
Or install Visual Studio Build Tools manually with the "Desktop development with C++" workload.
Basic Usage
import Database from "better-sqlite3";
const db = new Database("data.db");
// Create a table
db.exec(`
CREATE TABLE IF NOT EXISTS kv_store (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
)
`);
// Insert
const insert = db.prepare("INSERT OR REPLACE INTO kv_store (key, value) VALUES (?, ?)");
insert.run("mykey", "myvalue");
// Query
const row = db.prepare("SELECT value FROM kv_store WHERE key = ?").get("mykey");
console.log(row?.value); // "myvalue"
All operations are synchronous. db.prepare() creates a reusable statement. .run() executes writes, .get() returns one row, .all() returns all rows.
Docker
Native modules need to be compiled for the container's OS and architecture, not your host machine. Never copy node_modules from your host into a Docker image.
In your Dockerfile:
FROM node:20-slim
# Install build tools for native compilation
RUN apt-get update && apt-get install -y python3 build-essential && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
For multi-stage builds, compile in the builder stage and copy the built node_modules to the production stage. Both stages must use the same base architecture (don't build on node:20 and run on node:20-alpine — the native binaries won't be compatible unless you install musl-dev).
ARM (Raspberry Pi, Apple Silicon)
better-sqlite3 has prebuilds for ARM64. If they don't match your Node.js version, it falls back to compilation:
# Ensure build tools are present
sudo apt install build-essential python3
npm install better-sqlite3
On Apple Silicon Macs, Rosetta can cause architecture mismatches. Make sure your Node.js install matches your architecture — run node -p process.arch and confirm it says arm64, not x64.
Configuration Notes
- WAL mode: Enable Write-Ahead Logging for better concurrent read performance:
db.pragma('journal_mode = WAL'). Do this once right after opening the database. - Closing: Call
db.close()on process exit. Unclosed databases can corrupt the WAL file. Addprocess.on('exit', () => db.close()). - File path: Use an absolute path for the database file in production. A relative path is relative to
process.cwd(), which may not be what you expect in Docker or systemd. - TypeScript: Types are included. Import as
import Database from 'better-sqlite3'with"esModuleInterop": truein tsconfig, orimport * as Database from 'better-sqlite3'without it. - .gitignore: Add
*.dband*.db-walto your.gitignore. SQLite databases should not be committed to version control.