There's a restaurant down the street from me called Yaso Tangbao. They have delicious beef soup and dumplings with a fantastic ginger vinegar sauce. Everyone that I've brought there has said it was some of the best food they've ever had. Right next to it, there's a McDonalds. Millions of people go to McDonalds every day, but Yaso Tangbao attracts a much more modest following. McDonalds is certainly more popular. Does this mean it's better? The obvious answer is "no", it just has greater brand recognition.
Brand recognition, whether you like it or not, controls all of our lives, even in the open source marketplace. As another example, the popular test framework mocha was actually unmaintained for years. It had so many bugs at one point that the number of issues in its GitHub repo reached quadruple digits. Despite this, it remained the most popular test framework in Node.js. Why? Because it has brand name recognition. People trust mocha, regardless of objective truth about its quality. (now it is maintained again, because of popular demand).
Even the mysql driver for Node.js is more popular than mysql2, despite them sharing the same authors. The authors have been very vocal about mysql2 being the superior successor to mysql, but many people still don't even know mysql2 exists simply because mysql came first. Newcomers will still choose mysql because it appears first when searched at npmjs.com.
In open source, being first is more important than being better. I can't think of a single example of a Node.js package that is more popular than the one it's trying to replace, even if it's clearly superior.
In any case, here are the reasons to choose better-sqlite3:
1. It's bug-free
Despite node-sqlite3 having 3x as many stars, it has 10x as many issues in GitHub. If you search through them, you'll see countless issues about race conditions, data corruption, crashing, unexpected behavior, and more. Alternatively, if you search through the better-sqlite3 issues, you'll exclusively find installation issues and suggestions for enhancement. It's safe to assume that if you can install better-sqlite3, it will work as expected.
2. It's faster
See the benchmarks results, where the performance of better-sqlite3 and node-sqlite3 are compared.
3. It's simpler to use
Let's say we'd like to execute an atomic transaction that performs 2 steps:
- update some data
- read some data
The proper implementation in node-sqlite3 looks like this:
const { promisify } = require('util'); const { Database } = require('node-sqlite3'); // First, we have to promisify everything, since node-sqlite3 is callback-based const open = promisify((path, cb) => new Database(path, function (err) { cb(err, this); })); const get = promisify(Database.prototype.get); const run = promisify(Database.prototype.run); const close = promisify(Database.prototype.close); // To safely handle a transaction, we must open a separate database connection per transaction // See: https://github.com/mapbox/node-sqlite3/issues/304#issuecomment-45280758 async function myTransaction() { const db = await open('data.db'); let result; try { await run.call(db, 'BEGIN'); try { await run.call(db, 'UPDATE ...'); result = await get.call(db, 'SELECT ...'); await run.call(db, 'COMMIT'); } catch (err) { try { await run.call(db, 'ROLLBACK'); } catch (_) { /* manually ignore cases where the sqlite3 automatically rolled back the transaction */ } throw err; } } finally { await close.call(db); } return result; }
The proper implementation in better-sqlite3 looks like this:
const db = require('better-sqlite3')('data.db'); const myTransaction = db.transaction(() => { db.prepare('UPDATE ...').run(); return db.prepare('SELECT ...').get(); });
Not to mention, transactions in better-sqlite3 can automatically be nested. Can you imagine trying to write a nested transaction in node-sqlite3? Nightmare.
4. Features
In better-sqlite3, you can register custom functions and aggregate functions written in JavaScript, which you can run from within SQL queries.
In better-sqlite3, you can iterate through the cursor of a result set, and then stop whenever you want (you don't have to load the entire result set into memory).
In better-sqlite3, you can conveniently receive query results in many different formats (here, here, and here).
In better-sqlite3, you can safely work with SQLite's 64-bit integers, without losing precision due to JavaScript's number format.
None of the features listed above are possible in node-sqlite3.
Conclusion
better-sqlite3 wasn't first to the marketplace, so it doesn't have the brand recognition or years of legacy usage that node-sqlite3 has. But really, use whatever you want.
Reacted by Mike Escalante, Andrew Kaiser, Jeroen Claassens, Andrey Rublev, WillAvudim, Alex Rybakov, Michael Wills, punkish, zoubingwu, Greg Carter and 257 more
Reacted by Octo
Reacted by ehsan shahshahani, Daemon, Justin Chan, evercreeper, Soham Bhattacharyya, NemoStein, João Saraiva, Twilight, Cherry, Alexander Carls and 7 more
Reacted by Pranav Ramesh, Jan Doe, Twilight, TurtleIdiot, Michael Ford, Daemon, evercreeper, Soham Bhattacharyya, Wesley Bliss, João Saraiva and 11 more
Reacted by Wout Mertens, Alex Rybakov, punkish, samuba, tsemerad, Pedro Augusto de Paula Barbosa, Zach Zodkoy, Matthew McEachen, Girish Ramakrishnan, Gujie-Novade and 63 more
Reacted by Brian Harrington, punkish, Pedro Augusto de Paula Barbosa, Evgeniy Istomin, Luka Barbić, Pranav Ramesh, ⚠️ DO NOT USE! ⛔️, Twilight, MarxSoul55, TurtleIdiot and 23 more
Reacted by Dawadam, robertoslalom and Adrian Acurero