DEV Community

Cover image for Understanding Better-SQLite3: The Fastest SQLite Library for Node.js
Athreya aka Maneshwar
Athreya aka Maneshwar

Posted on

Understanding Better-SQLite3: The Fastest SQLite Library for Node.js

Hello, I'm Maneshwar. I'm working on FreeDevTools online currently building *one place for all dev tools, cheat codes, and TLDRs* — a free, open-source hub where developers can quickly find and use tools without any hassle of searching all over the internet.

If you’ve ever needed a lightweight and super-fast database for a Node.js project, chances are you’ve looked at SQLite.

It’s self-contained, serverless, and simple to use — perfect for apps, tools, and local data storage.

But when it comes to using SQLite with Node.js, not all libraries are created equal.

That’s where Better-SQLite3 comes in.

What is Better-SQLite3?

Better-SQLite3 is a Node.js library that provides a fast, safe, and simple way to work with SQLite databases.

Unlike other SQLite bindings such as node-sqlite3, it offers a fully synchronous API — and yet it still manages to outperform most async wrappers.

It’s designed for developers who want performance and simplicity, especially when dealing with local data or small to medium datasets.

Installation

npm install better-sqlite3
Enter fullscreen mode Exit fullscreen mode

Requirements: Node.js v14.21.1 or later (LTS recommended).

If installation fails, check the library’s troubleshooting guide — prebuilt binaries are available for common environments.

Basic Usage

const Database = require('better-sqlite3');
const db = new Database('foobar.db');

// Enable WAL mode for performance
db.pragma('journal_mode = WAL');

// Query example
const row = db.prepare('SELECT * FROM users WHERE id = ?').get(1);
console.log(row.firstName, row.lastName, row.email);
Enter fullscreen mode Exit fullscreen mode

Or using ES Modules:

import Database from 'better-sqlite3';
const db = new Database('foobar.db');
db.pragma('journal_mode = WAL');
Enter fullscreen mode Exit fullscreen mode

Why Better-SQLite3 Is Different

1. Sync but faster than async

node-sqlite3 uses asynchronous APIs for serialized operations — which actually hurts performance.
better-sqlite3 executes everything synchronously, avoiding event loop complexity and improving concurrency.

2. Memory management done the JavaScript way

Instead of exposing raw C-level memory operations, it relies on Node’s garbage collector.

3. Cleaner API and utilities

Tasks that are complex or impossible in node-sqlite3 are straightforward here — transactions, backups, and custom functions are first-class citizens.

4. Performance

Benchmarks show it outperforms node-sqlite3 in almost all cases, and matches it in the rest.

When Not to Use It

SQLite — and therefore Better-SQLite3 — has limits. You might not want to use it if:

  • You have many concurrent writes (e.g., a social media platform).
  • You’re serving large read data (e.g., video streams).
  • Your database is approaching terabyte size.

In these cases, a full-scale RDBMS like PostgreSQL or MySQL is a better choice.

The Database Class

Creating a new database connection:

const db = new Database('foobar.db', {
  readonly: false,
  fileMustExist: false,
  timeout: 5000,
  verbose: console.log
});
Enter fullscreen mode Exit fullscreen mode
  • readonly — open DB in read-only mode.
  • fileMustExist — throw an error if the DB file doesn’t exist.
  • timeout — how long to wait on a locked DB (default: 5 seconds).
  • verbose — log every SQL query executed.

To create an in-memory database:

const db = new Database(':memory:');
Enter fullscreen mode Exit fullscreen mode

Transactions Made Easy

Transactions wrap multiple queries into a single atomic operation:

const insert = db.prepare('INSERT INTO cats (name, age) VALUES (?, ?)');
const insertMany = db.transaction((cats) => {
  for (const cat of cats) insert.run(cat.name, cat.age);
});

insertMany([
  { name: 'Joey', age: 2 },
  { name: 'Sally', age: 4 }
]);
Enter fullscreen mode Exit fullscreen mode

Transactions can also be nested and come with different modes:

insertMany.deferred(cats);
insertMany.immediate(cats);
insertMany.exclusive(cats);
Enter fullscreen mode Exit fullscreen mode

If an inner transaction fails, it automatically rolls back to its previous savepoint.

Other Useful APIs

  • db.prepare(sql) → returns a Statement for repeated execution.
  • db.transaction(fn) → runs a function inside a transaction.
  • db.backup(path) → backs up the database file.
  • db.serialize() / db.deserialize() → convert the database to/from a buffer.
  • db.function() / db.aggregate() → define custom SQL functions.
  • db.loadExtension() → load SQLite C extensions.
  • db.close() → safely close the database.

Final Thoughts

Better-SQLite3 is ideal when you want:

  • A local, fast, and reliable database for your Node.js app.
  • Synchronous access without async complexity.
  • High-performance data handling for tools, scripts, or embedded systems.

It’s not meant to replace enterprise databases — but for most small to medium projects, it’s a perfect balance of speed, simplicity, and control.

FreeDevTools

I’ve been building for FreeDevTools.

A collection of UI/UX-focused tools crafted to simplify workflows, save time, and reduce friction in searching tools/materials.

Any feedback or contributors are welcome!

It’s online, open-source, and ready for anyone to use.

👉 Check it out: FreeDevTools
⭐ Star it on GitHub: freedevtools

Top comments (0)