DocsDatabases

SQLite with node:sqlite

Using SQLite in Deno via node:sqlite and compatible libraries, with least-privilege and WAL/backup boundaries

Deno has shipped node:sqlite in its Node compatibility layer since v2.2, and the official Node API docs list it as fully supported. Upstream, Node added the module in v22.5.0 and—as of 2026-08-03—still labels it Stability 1.2 (release candidate), so minor API changes remain possible before full stabilization. Recheck after Deno upgrades.

import { DatabaseSync } from "node:sqlite";

const db = new DatabaseSync("./data/app.db");

db.exec(`
  PRAGMA journal_mode = WAL;
  PRAGMA busy_timeout = 5000;
  PRAGMA foreign_keys = ON;

  CREATE TABLE IF NOT EXISTS posts (
    id INTEGER PRIMARY KEY,
    title TEXT NOT NULL
  ) STRICT;
`);

const insert = db.prepare("INSERT INTO posts (title) VALUES (?)");
insert.run("Hello Deno");

const rows = db.prepare("SELECT id, title FROM posts").all();

Every DatabaseSync API runs synchronously, which suits scripts, CLIs, and single-writer services. Do not put long synchronous queries on a high-concurrency request path.

Alternatives

LibraryFormNotes
node:sqlitebuilt-in Node compat moduleno dependency to install; upstream still a release candidate
jsr:@db/sqliteFFI loading a prebuilt native libraryits README requires --allow-ffi and --allow-env, plus network and file permissions to download and cache the native library
npm:better-sqlite3Node native addonrelies on Deno's native addon support; verify against your Deno version

FFI and native addons load machine code that JavaScript-level permissions cannot sandbox; count that in your trust boundary. Pure WASM options such as npm:sql.js avoid FFI, but the database lives in memory and persistence is your own problem.

When SQLite fits

  • Single-node services, CLI tools, desktop or single-instance edge apps.
  • Tests and local development: isolate each test with :memory: or a temp file.
  • Read-heavy embedded workloads, with WAL to overlap reads and writes.
  • Multi-region or edge replicas are the domain of LiteFS/libsql-style solutions, outside node:sqlite's scope—evaluate them separately.

Least privilege

deno run --allow-read=./data --allow-write=./data src/main.ts
  • WAL mode creates app.db-wal and app.db-shm sidecar files. Granting write access to exactly app.db fails at checkpoint or first write; grant the directory, or list all three files.
  • Read-only tools should open with new DatabaseSync(path, { readOnly: true }) and request only --allow-read.
  • FFI-based options like jsr:@db/sqlite effectively need near -A trust; do not use narrow grants to reassure yourself.

WAL, backup, and concurrent-write boundaries

  • SQLite has a single writer: WAL lets reads overlap with one write, but writes still serialize. Keep write transactions short; busy_timeout controls how long lock contention waits before raising SQLITE_BUSY.
  • Multiple processes may open the same file, but write throughput does not scale. Never run SQLite on network filesystems such as NFS.
  • Run PRAGMA wal_checkpoint(TRUNCATE) before a file-copy backup, or the copy misses commits still in the WAL; alternatively use the official SQLite CLI .backup.
  • Upstream node:sqlite added sqlite.backup() in Node v23.8.0 / v22.16.0; verify support on your Deno version before relying on it.
  • Treat migrations as a release step, as with PostgreSQL—schema changes at app startup multiply concurrency risk across instances.

Official references: Deno Node API support, Node.js node:sqlite docs, jsr:@db/sqlite, SQLite WAL mode.

Type to search all documentation.