3秒でわかる
Node.js から SQLite を同期 API で扱うライブラリ。await 無しで SQL を書け、ローカルツールやテスト用 DB として広く使われます。
もう少し詳しく
どういうものか
better-sqlite3 は、Node.js から SQLite のデータベースファイルを読み書きするためのライブラリです。最大の特徴は API が同期であることで、await も コールバックも書かずに戻り値をそのまま受け取れます。
db.prepare(sql) で文を用意し、.run() で更新、.get() で 1 行取得、.all() で全行取得と、メソッドが用途で分かれています。データベースは 1 つのファイルなので、サーバープロセスの起動も接続設定も要りません。
なぜ必要か
Node.js の DB クライアントは非同期が主流ですが、SQLite はネットワーク越しではなくローカルファイルを直接読むため、待ち時間の性質がまったく違います。ネットワーク待ちが無い処理を非同期にしても、await の記述が増えるだけで得るものがありません。同期 API はこの状況に素直に合っており、try/catch での例外処理やトランザクションの書き方も単純になります。
一方で同期であることは、その間 Node.js のイベントループが止まることを意味します。1 リクエストごとに数ミリ秒の問い合わせをする用途では問題になりませんが、数十万行を一度に走査するような処理を HTTP サーバーの中で行うと、他のリクエストがすべて待たされます。
具体例
テーブル作成から取得までが数行で完結します。
import Database from "better-sqlite3";
const db = new Database("app.db");
db.pragma("journal_mode = WAL"); // 書き込み性能が大きく上がる
db.exec(`
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
done INTEGER NOT NULL DEFAULT 0
)
`);
const insert = db.prepare("INSERT INTO tasks (title) VALUES (?)");
insert.run("買い出し");
// 複数件をまとめるときはトランザクションで包む
const insertMany = db.transaction((titles) => {
for (const t of titles) insert.run(t);
});
insertMany(["洗濯", "掃除", "請求書の確認"]);
const rows = db.prepare("SELECT * FROM tasks WHERE done = ?").all(0);
console.log(rows.length); // 4つまずきやすいところ
NODE_MODULE_VERSION の不一致エラーが出たら npm rebuild better-sqlite3 を実行します? のプレースホルダを使えば SQL インジェクションを防げるうえ、prepare した文を使い回せて速くなりますINSERT を裸で回して極端に遅くなる。1 件ごとにディスクへ書き込むためで、db.transaction() で包むと数十倍変わります1 と 0 に変換して渡します似た用語との違い
Node.js には同種のことができる標準モジュールが同梱されており、外部依存なしに使えます。Bun が内蔵する SQLite モジュールも API が近い設計です。better-sqlite3 は歴史が長く、Electron やツール類での採用例と情報量が多い点が利点になります。
覚え方
run は書き込み、get は 1 行、all は全行。この 3 つとトランザクションを覚えれば、日常の用途はまかなえます。