SQLite Driver Benchmark: Comparing better-sqlite3, node:sqlite, libSQL, Turso

4 min read Original article ↗

Choosing the right SQLite driver for Node.js can impact your application’s performance.

We ran performance tests comparing better-sqlite3, node:sqlite, libSQL, and Turso across common database operations. Here’s what we found.

The Four SQLite Options for Node.js

Section titled “The Four SQLite Options for Node.js”

This library has been tested in production for years, offering excellent performance through a synchronous API. The synchronous API makes it very performant and easy to use, and is perfect for fast queries.

GitHub better-sqlite3

Node.js 22+ includes a built-in SQLite module (still experimental). It provides zero-dependency SQLite access with a synchronous API similar to better-sqlite3, and also an asynchronous API.

Node.js SQlite Documentation

An open-source fork of SQLite created by the Turso team. It provides an async API and supports both local database files and remote libSQL/Turso servers. Fully compatible with the original SQLite (file format, API)

libSQL TypeScript

Turso is a SQLite compatible database written in Rust. Currently still in beta.

Turso for Javascript

Benchmark Methodology

Section titled “Benchmark Methodology”

We tested all four drivers with identical queries. A simple scenario with a user and posts table.

  • 10,000 users and 500,000 posts (50 per user)
  • Optimized pragma settings (WAL mode, 64MB cache, memory-mapped I/O)
  • Various query patterns: simple selects, indexed lookups, JOINs, aggregates, inserts, updates

Note: better-sqlite3 and node:sqlite use synchronous APIs, while libSQL and Turso use async/await.

SQLite Configuration

Section titled “SQLite Configuration”

All databases used these optimized settings:

PRAGMA journal_mode = WAL; -- Write-Ahead Logging

PRAGMA synchronous = NORMAL; -- Balance safety/speed

PRAGMA cache_size = -64000; -- 64MB cache

PRAGMA temp_store = MEMORY; -- Temp tables in memory

PRAGMA mmap_size = 268435456; -- 256MB memory-mapped I/O

SQLite Driver Benchmark Report

Comparing better-sqlite3 vs node:sqlite vs libsql vs turso (baseline: node:sqlite)

System Information

Node.js Versionv25.3.0
Platformlinux
Architecturex64
CPU12th Gen Intel(R) Core(TM) i9-12900K
CPU Cores24
Total Memory31.07 GB

Summary

Operation better-sqlite3 node:sqlite libsql turso Winner vs node:sqlite
getAllUsers 360 ops/s 268 ops/s 50 ops/s 104 ops/s better-sqlite3 1.34x
getUserById 1,223,260 ops/s 1,073,001 ops/s 61,093 ops/s 707,859 ops/s better-sqlite3 1.14x
getUserByEmail 557,631 ops/s 457,659 ops/s 49,510 ops/s 233,913 ops/s better-sqlite3 1.22x
countUsers (pluck) 538,031 ops/s 398,431 ops/s 108,632 ops/s 5,593 ops/s better-sqlite3 1.35x
getPostsByUser 1,090,293 ops/s 980,550 ops/s 47,304 ops/s 414,672 ops/s better-sqlite3 1.11x
getPublishedPosts (JOIN) 27 ops/s 27 ops/s 54 ops/s 7 ops/s libsql 1.98x
getPostWithAuthor (JOIN :one) 477,271 ops/s 379,911 ops/s 32,433 ops/s 236,297 ops/s better-sqlite3 1.26x
countPostsByUser (pluck) 1,151,783 ops/s 689,478 ops/s 111,824 ops/s 377,235 ops/s better-sqlite3 1.67x
insertUser 53,693 ops/s 41,291 ops/s 28,385 ops/s 63,017 ops/s turso 1.53x
updatePostViews 136,399 ops/s 97,956 ops/s 53,598 ops/s 59,273 ops/s better-sqlite3 1.39x
  • better-sqlite3 is the fastest for most operations, with node:sqlite second
  • Turso has a surprisingly slow query for countPostsByUser (better-sqlite3 is almost 100x faster here). I did not investigate why this is, it might be that the Turso database like many others (eg Postgresql) needs to scan the full table in order to count the number of rows and has no fast handling for this special case.

Benchmark Implementation

Section titled “Benchmark Implementation”

The benchmark is implemented using SQG, a SQL to code generator.

One advantage of using a code generator like SQG is that you can switch between SQLite drivers without rewriting your queries. SQG generates type-safe code for all four drivers from the same SQL file:

# sqg.yaml - generate code for multiple drivers

sql:

- files:

- queries.sql

gen:

- generator: typescript/sqlite/better-sqlite3

output: ./src/db-better-sqlite3.ts

- generator: typescript/sqlite/node

output: ./src/db-node-sqlite.ts

- generator: typescript/sqlite/libsql

output: ./src/db-libsql.ts

- generator: typescript/sqlite/turso

output: ./src/db-turso.ts

This makes it easy to benchmark with your actual queries and switch drivers by changing imports.

The benchmark code is available in our examples repository:

git clone https://github.com/sqg-dev/sqg

cd sqg/examples/typescript-sqlite-benchmark

pnpm install

pnpm generate

pnpm bench

This will generate an HTML report with the results table shown above.

I think for most applications, better-sqlite3 remains the best choice.


Benchmarks generated using SQG. Have questions? Open an issue on GitHub.