Infrastructure

Better-SQLite3SetupforNode.js

Install and configure better-sqlite3 for Node.js projects. Covers native compilation issues, Docker builds, ARM compatibility, and WAL mode setup.

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. Add process.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": true in tsconfig, or import * as Database from 'better-sqlite3' without it.
  • .gitignore: Add *.db and *.db-wal to your .gitignore. SQLite databases should not be committed to version control.