Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
node-version: "24"

- name: Cache node_modules
uses: actions/cache@v4
Expand All @@ -33,9 +33,9 @@ jobs:
~/.nvm
~/work/better-sqlite3-wrapper/better-sqlite3-wrapper/node_modules
~/work/better-sqlite3-wrapper/better-sqlite3-wrapper/package-lock.json
key: ${{ runner.os }}-node_modules-cache-v4-${{ hashFiles('**/package-lock.json') }}
key: ${{ runner.os }}-24-node_modules-cache-v4-${{ hashFiles('package.json') }}
restore-keys: |
${{ runner.os }}-node_modules-cache-v4-
${{ runner.os }}-24-node_modules-cache-v4-

- name: Extract Tag Name
run: echo "TAG_NAME=$(echo ${GITHUB_REF##*/})" >> $GITHUB_ENV
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci_bun.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Git checkout
uses: actions/checkout@v4
Expand Down
66 changes: 66 additions & 0 deletions .github/workflows/ci_matrix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Node Matrix CI

on:
push:
paths-ignore:
- "*.md"
branches: [main]
pull_request:
branches: [main]

jobs:
build:
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-2019
node:
- 16
- 18
- 20
- 22
name: Testing Node ${{ matrix.node }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
timeout-minutes: 20
steps:
- name: Git checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}

- name: Cache node_modules on non-windows os
if: ${{ !startsWith(matrix.os, 'windows') }}
uses: actions/cache@v4
with:
path: |
~/.npm
~/.nvm
~/work/better-sqlite3-wrapper/better-sqlite3-wrapper/node_modules
~/work/better-sqlite3-wrapper/better-sqlite3-wrapper/package-lock.json
key: ${{ runner.os }}-${{ matrix.node }}-node_modules-cache-v1-${{ hashFiles('package.json') }}
restore-keys: |
${{ runner.os }}-${{ matrix.node }}-node_modules-cache-v1-

- name: Cache node_modules on windows os
if: ${{ startsWith(matrix.os, 'windows') }}
uses: actions/cache@v4
with:
path: |
~/AppData/Roaming/npm
D:/a/better-sqlite3-wrapper/better-sqlite3-wrapper/node_modules
D:/a/better-sqlite3-wrapper/better-sqlite3-wrapper/package-lock.json
key: ${{ runner.os }}-${{ matrix.node }}-node_modules-cache-v1-${{ hashFiles('package.json') }}
restore-keys: |
${{ runner.os }}-${{ matrix.node }}-node_modules-cache-v1-

- name: Run tests
run: |
npm i
node ./index.test.js
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@

[![CI](https://github.com/farjs/better-sqlite3-wrapper/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/farjs/better-sqlite3-wrapper/actions/workflows/ci.yml?query=workflow%3Aci+branch%3Amain)
[![npm version](https://img.shields.io/npm/v/@farjs/better-sqlite3-wrapper)](https://www.npmjs.com/package/@farjs/better-sqlite3-wrapper)

## @farjs/better-sqlite3-wrapper

Thin api wrapper around [better-sqlite3](https://github.com/WiseLibs/better-sqlite3)
and [bun:sqlite](https://bun.sh/docs/api/sqlite) to allow cross- runtime/engine usage.
Thin api wrapper around:

- [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) on node version < 22
- [node:sqlite](https://nodejs.org/docs/latest-v22.x/api/sqlite.html) on node version >= 22
- and [bun:sqlite](https://bun.sh/docs/api/sqlite)

to allow cross- runtime/engine usage.

It exposes only [a subset](./index.d.ts) of `better-sqlite3` api because not everything
is available in `bun:sqlite`.
is available in `bun:sqlite`/`node:sqlite`.

For example, since `run` returns `undefined` you can use the following queries to get
`changes` and/or `lastInsertRowid`:
`changes` and/or `lastInsertRowid`:

```javascript
const changes = db.prepare("SELECT changes() AS changes;").get().changes;
const lastInsertRowId = db.prepare("SELECT last_insert_rowid() AS id;").get().id;
const lastInsertRowId = db
.prepare("SELECT last_insert_rowid() AS id;")
.get().id;
```

Also, since `db.pragma` is not available in `bun:sqlite`, you can run:
Expand Down
65 changes: 64 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,70 @@ const wrapper = function () {
return require("bun:sqlite").Database;
}

return require("better-sqlite3");
if (isModuleAvailable("better-sqlite3")) {
return require("better-sqlite3");
}

const { DatabaseSync } = require("node:sqlite");
function dbCtor(path, options) {
if (options) {
return nodeDbWrapper(new DatabaseSync(path, options));
}

return nodeDbWrapper(new DatabaseSync(path));
}
return dbCtor;
};

function isModuleAvailable(name) {
try {
require.resolve(name);
return true;
} catch {}

return false;
}

function nodeDbWrapper(db) {
//check if node supports it
//see: https://github.com/nodejs/node/issues/57431
if (db.transaction) {
return db;
}

//see: https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#transactionfunction---function
db.transaction = (f) => {
const doTx = (...args) => {
try {
const res = f(...args);
db.prepare("COMMIT").run();
return res;
} catch (error) {
db.prepare("ROLLBACK").run();
throw error;
}
};

const txFn = (...args) => {
db.prepare("BEGIN").run();
return doTx(...args);
};
txFn.deferred = (...args) => {
db.prepare("BEGIN DEFERRED").run();
return doTx(...args);
};
txFn.immediate = (...args) => {
db.prepare("BEGIN IMMEDIATE").run();
return doTx(...args);
};
txFn.exclusive = (...args) => {
db.prepare("BEGIN EXCLUSIVE").run();
return doTx(...args);
};

return txFn;
};
return db;
}

module.exports = wrapper();
83 changes: 71 additions & 12 deletions index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ it("should open in-memory database", () => {
const query = db.prepare("select 'Hello world' as message;");

//then
assert.deepEqual(query.get(), { message: "Hello world" });
assert.deepEqual({ ...query.get() }, { message: "Hello world" });

//when & then
const results = query.all();
assert.deepEqual(Array.isArray(results), true);
assert.deepEqual(results, [{ message: "Hello world" }]);
assert.deepEqual(
results.map((r) => {
return { ...r };
}),
[{ message: "Hello world" }]
);

//when & then
db.close();
Expand Down Expand Up @@ -62,19 +67,73 @@ it("should create file-based database", () => {
const results = db.prepare("select * from test order by id;").all();

//then
assert.deepEqual(results, [
{
id: 1,
name: "test1",
},
{
id: 2,
name: "test2",
},
]);
assert.deepEqual(
results.map((r) => {
return { ...r };
}),
[
{
id: 1,
name: "test1",
},
{
id: 2,
name: "test2",
},
]
);

//cleanup
db.close();
fs.unlinkSync(file);
fs.rmdirSync(tmpDir);
});

it("should rollback failed transaction", () => {
//when
const db = new Database(":memory:");
const changesQuery = db.prepare("SELECT changes() AS changes;");
const lastInsertRowIdQuery = db.prepare("SELECT last_insert_rowid() AS id;");
db.prepare(
`
create table test(
id integer primary key,
name text not null
);
`
).run();
assert.deepEqual(changesQuery.get().changes, 0);
assert.deepEqual(lastInsertRowIdQuery.get().id, 0);

const error = Error("test error");
const txFn = db.transaction((name) => {
const insert = db.prepare("insert into test (name) values (?);");
insert.run(name);
throw error;
});
let caughtError = null;

//when
try {
txFn("test1");
} catch (err) {
caughtError = err;
}

//then
assert.deepEqual(caughtError === error, true);
assert.deepEqual(changesQuery.get().changes, 1);
assert.deepEqual(lastInsertRowIdQuery.get().id, 1);

//when & then
const query = db.prepare("select id, name from test;");
assert.deepEqual(
query.all().map((r) => {
return { ...r };
}),
[]
);

//cleanup
db.close();
});
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@
},
"browserslist": "maintained node versions",
"engines": {
"node": ">=18",
"node": ">=16",
"bun": ">=0.8"
},
"optionalDependencies": {
"better-sqlite3": "^11.5.0"
"better-sqlite3": "8.7.0"
},
"devDependencies": {
"prettier": "^2.8.8",
Expand Down
Loading