Constants from sqlite3.h
This list isn't exhaustive, but some of the ones which are relevant
module
The 'bun:sqlite'
module is a high-performance SQLite3 driver built directly into Bun. The API is simple, synchronous, and inspired by better-sqlite3, offering a clean and intuitive interface for database operations.
Key features include prepared statements, transactions, both named and positional parameters, datatype conversions (BLOB to Uint8Array), mapping query results to classes, and support for bigint. Multi-query statements can be executed in a single call to database.run().
Performance benchmarks show bun:sqlite is approximately 3-6x faster than better-sqlite3 and 8-9x faster than deno.land/x/sqlite for read queries, making it the fastest SQLite driver available for JavaScript.
Constants from sqlite3.h
This list isn't exhaustive, but some of the ones which are relevant
Allow creating a new database
Open the database as read-only (no write operations, no create).
Open the database for reading and writing
A SQLite3 database
const db = new Database("mydb.sqlite");
db.run("CREATE TABLE foo (bar TEXT)");
db.run("INSERT INTO foo VALUES (?)", ["baz"]);
console.log(db.query("SELECT * FROM foo").all());
The filename passed when new Database()
was called
const db = new Database("mydb.sqlite");
console.log(db.filename);
// => "mydb.sqlite"
The underlying sqlite3
database handle
In native code, this is not a file descriptor, but an index into an array of database handles
Close the database connection.
It is safe to call this method multiple times. If the database is already closed, this is a no-op. Running queries after the database has been closed will throw an error.
If true
, then the database will throw an error if it is in use
db.close();
This is called automatically when the database instance is garbage collected.
Internally, this calls sqlite3_close_v2
.
This is an alias of Database.run
See sqlite3_file_control
for more information.
See sqlite3_file_control
for more information.
Load a SQLite3 extension
macOS requires a custom SQLite3 library to be linked because the Apple build of SQLite for macOS disables loading extensions. See Database.setCustomSQLite
Bun chooses the Apple build of SQLite on macOS because it brings a ~50% performance improvement.
name/path of the extension to load
optional entry point of the extension
Compile a SQL query and return a Statement object.
This does not cache the compiled query and does not execute the query.
Under the hood, this calls sqlite3_prepare_v3
.
The SQL query to compile
Optional bindings for the query
A Statement instance
// compile the query
const stmt = db.query("SELECT * FROM foo WHERE bar = ?");
// run the query
stmt.all("baz");
Compile a SQL query and return a Statement object. This is the same as prepare except that it caches the compiled query.
This does not execute the query, but instead prepares it for later execution and caches the compiled query if possible.
Under the hood, this calls sqlite3_prepare_v3
.
The SQL query to compile
Statment
instance
// compile the query
const stmt = db.query("SELECT * FROM foo WHERE bar = ?");
// run the query
stmt.all("baz");
// run the query again
stmt.all();
Execute a SQL query without returning any results.
This does not cache the query, so if you want to run a query multiple times, you should use prepare instead.
Under the hood, this calls sqlite3_prepare_v3
followed by sqlite3_step
and sqlite3_finalize
.
The following types can be used when binding parameters:
JavaScript type | SQLite type |
---|---|
string | TEXT |
number | INTEGER or DECIMAL |
boolean | INTEGER (1 or 0) |
Uint8Array | BLOB |
Buffer | BLOB |
bigint | INTEGER |
null | NULL |
The SQL query to run
Optional bindings for the query
Database
instance
db.run("CREATE TABLE foo (bar TEXT)");
db.run("INSERT INTO foo VALUES (?)", ["baz"]);
Useful for queries like:
CREATE TABLE
INSERT INTO
UPDATE
DELETE FROM
DROP TABLE
PRAGMA
ATTACH DATABASE
DETACH DATABASE
REINDEX
VACUUM
EXPLAIN ANALYZE
CREATE INDEX
CREATE TRIGGER
CREATE VIEW
CREATE VIRTUAL TABLE
CREATE TEMPORARY TABLE
Creates a function that always runs inside a transaction. When the function is invoked, it will begin a new transaction. When the function returns, the transaction will be committed. If an exception is thrown, the transaction will be rolled back (and the exception will propagate as usual).
The callback which runs inside a transaction
// setup
import { Database } from "bun:sqlite";
const db = Database.open(":memory:");
db.exec(
"CREATE TABLE cats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER)"
);
const insert = db.prepare("INSERT INTO cats (name, age) VALUES ($name, $age)");
const insertMany = db.transaction((cats) => {
for (const cat of cats) insert.run(cat);
});
insertMany([
{ $name: "Joey", $age: 2 },
{ $name: "Sally", $age: 4 },
{ $name: "Junior", $age: 1 },
]);
Load a serialized SQLite3 database
Internally, this calls sqlite3_deserialize
.
Data to load
Database
instance
test("supports serialize/deserialize", () => {
const db = Database.open(":memory:");
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)");
db.exec('INSERT INTO test (name) VALUES ("Hello")');
db.exec('INSERT INTO test (name) VALUES ("World")');
const input = db.serialize();
const db2 = new Database(input);
const stmt = db2.prepare("SELECT * FROM test");
expect(JSON.stringify(stmt.get())).toBe(
JSON.stringify({
id: 1,
name: "Hello",
}),
);
expect(JSON.stringify(stmt.all())).toBe(
JSON.stringify([
{
id: 1,
name: "Hello",
},
{
id: 2,
name: "World",
},
]),
);
db2.exec("insert into test (name) values ('foo')");
expect(JSON.stringify(stmt.all())).toBe(
JSON.stringify([
{
id: 1,
name: "Hello",
},
{
id: 2,
name: "World",
},
{
id: 3,
name: "foo",
},
]),
);
const db3 = Database.deserialize(input, true);
try {
db3.exec("insert into test (name) values ('foo')");
throw new Error("Expected error");
} catch (e) {
expect(e.message).toBe("attempt to write a readonly database");
}
});
Load a serialized SQLite3 database. This version enables you to specify additional options such as strict
to put the database into strict mode.
Internally, this calls sqlite3_deserialize
.
Data to load
Database
instance
test("supports serialize/deserialize", () => {
const db = Database.open(":memory:");
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)");
db.exec('INSERT INTO test (name) VALUES ("Hello")');
db.exec('INSERT INTO test (name) VALUES ("World")');
const input = db.serialize();
const db2 = Database.deserialize(input, { strict: true });
const stmt = db2.prepare("SELECT * FROM test");
expect(JSON.stringify(stmt.get())).toBe(
JSON.stringify({
id: 1,
name: "Hello",
}),
);
expect(JSON.stringify(stmt.all())).toBe(
JSON.stringify([
{
id: 1,
name: "Hello",
},
{
id: 2,
name: "World",
},
]),
);
db2.exec("insert into test (name) values ($foo)", { foo: "baz" });
expect(JSON.stringify(stmt.all())).toBe(
JSON.stringify([
{
id: 1,
name: "Hello",
},
{
id: 2,
name: "World",
},
{
id: 3,
name: "baz",
},
]),
);
const db3 = Database.deserialize(input, { readonly: true, strict: true });
try {
db3.exec("insert into test (name) values ($foo)", { foo: "baz" });
throw new Error("Expected error");
} catch (e) {
expect(e.message).toBe("attempt to write a readonly database");
}
});
The path to the SQLite library
Errors from SQLite have a name SQLiteError
.
The UTF-8 byte offset of the sqlite3 query that failed, if known
This corresponds to sqlite3_error_offset
.
Optional override for formatting stack traces
Creates a .stack
property on targetObject
, which when accessed returns a string representing the location in the code at which Error.captureStackTrace()
was called.
const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // Similar to `new Error().stack`
The first line of the trace will be prefixed with ${myObject.name}: ${myObject.message}
.
The optional constructorOpt
argument accepts a function. If given, all frames above constructorOpt
, including constructorOpt
, will be omitted from the generated stack trace.
The constructorOpt
argument is useful for hiding implementation details of error generation from the user. For instance:
function a() {
b();
}
function b() {
c();
}
function c() {
// Create an error without stack trace to avoid calculating the stack trace twice.
const { stackTraceLimit } = Error;
Error.stackTraceLimit = 0;
const error = new Error();
Error.stackTraceLimit = stackTraceLimit;
// Capture the stack trace above function b
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
throw error;
}
a();
A prepared statement.
This is returned by Database.prepare and Database.query.
const stmt = db.prepare("SELECT * FROM foo WHERE bar = ?");
stmt.all("baz");
// => [{bar: "baz"}]
The names of the columns returned by the prepared statement.
const stmt = db.prepare("SELECT bar FROM foo WHERE bar = ?");
console.log(stmt.columnNames);
// => ["bar"]
Native object representing the underlying sqlite3_stmt
This is left untyped because the ABI of the native bindings may change at any time.
The number of parameters expected in the prepared statement.
const stmt = db.prepare("SELECT * FROM foo WHERE bar = ?");
console.log(stmt.paramsCount);
// => 1
Calls finalize if it wasn't already called.
Execute the prepared statement and return all results as objects.
optional values to bind to the statement. If omitted, the statement is run with the last bound values or no parameters if there are none.
const stmt = db.prepare("SELECT * FROM foo WHERE bar = ?");
stmt.all("baz");
// => [{bar: "baz"}]
stmt.all();
// => []
stmt.all("foo");
// => [{bar: "foo"}]
Make get and all return an instance of the provided Class
instead of the default Object
.
A class to use
The same statement instance, modified to return an instance of Class
This lets you attach methods, getters, and setters to the returned objects.
For performance reasons, constructors for classes are not called, which means initializers will not be called and private fields will not be accessible.
class User {
rawBirthdate: string;
get birthdate() {
return new Date(this.rawBirthdate);
}
}
const db = new Database(":memory:");
db.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, rawBirthdate TEXT)");
db.run("INSERT INTO users (rawBirthdate) VALUES ('1995-12-19')");
const query = db.query("SELECT * FROM users");
query.as(User);
const user = query.get();
console.log(user.birthdate);
// => Date(1995, 12, 19)
Finalize the prepared statement, freeing the resources used by the statement and preventing it from being executed again.
This is called automatically when the prepared statement is garbage collected.
It is safe to call this multiple times. Calling this on a finalized statement has no effect.
Internally, this calls sqlite3_finalize
.
Execute the prepared statement and return the first result.
If no result is returned, this returns null
.
optional values to bind to the statement. If omitted, the statement is run with the last bound values or no parameters if there are none.
const stmt = db.prepare("SELECT * FROM foo WHERE bar = ?");
stmt.get("baz");
// => {bar: "baz"}
stmt.get();
// => null
stmt.get("foo");
// => {bar: "foo"}
The following types can be used when binding parameters:
JavaScript type | SQLite type |
---|---|
string | TEXT |
number | INTEGER or DECIMAL |
boolean | INTEGER (1 or 0) |
Uint8Array | BLOB |
Buffer | BLOB |
bigint | INTEGER |
null | NULL |
Execute the prepared statement and return an
optional values to bind to the statement. If omitted, the statement is run with the last bound values or no parameters if there are none.
optional values to bind to the statement. If omitted, the statement is run with the last bound values or no parameters if there are none.
const stmt = db.prepare("UPDATE foo SET bar = ?");
stmt.run("baz");
// => undefined
stmt.run();
// => undefined
stmt.run("foo");
// => undefined
The following types can be used when binding parameters:
JavaScript type | SQLite type |
---|---|
string | TEXT |
number | INTEGER or DECIMAL |
boolean | INTEGER (1 or 0) |
Uint8Array | BLOB |
Buffer | BLOB |
bigint | INTEGER |
null | NULL |
Return the expanded SQL string for the prepared statement.
Internally, this calls sqlite3_expanded_sql()
on the underlying sqlite3_stmt
.
const stmt = db.prepare("SELECT * FROM foo WHERE bar = ?", "baz");
console.log(stmt.toString());
// => "SELECT * FROM foo WHERE bar = 'baz'"
console.log(stmt);
// => "SELECT * FROM foo WHERE bar = 'baz'"
Execute the prepared statement and return the results as an array of arrays.
In Bun v0.6.7 and earlier, this method returned null
if there were no results instead of []
. This was changed in v0.6.8 to align more with what people expect.
optional values to bind to the statement. If omitted, the statement is run with the last bound values or no parameters if there are none.
const stmt = db.prepare("SELECT * FROM foo WHERE bar = ?");
stmt.values("baz");
// => [['baz']]
stmt.values();
// => [['baz']]
stmt.values("foo");
// => [['foo']]
stmt.values("not-found");
// => []
The following types can be used when binding parameters:
JavaScript type | SQLite type |
---|---|
string | TEXT |
number | INTEGER or DECIMAL |
boolean | INTEGER (1 or 0) |
Uint8Array | BLOB |
Buffer | BLOB |
bigint | INTEGER |
null | NULL |
The native module implementing the sqlite3 C bindings
It is lazily-initialized, so this will return undefined
until the first call to new Database().
The native module makes no gurantees about ABI stability, so it is left untyped
If you need to use it directly for some reason, please let us know because that probably points to a deficiency in this API.
An object representing the changes made to the database since the last run
or exec
call.
If safeIntegers
is true
, this is a bigint
. Otherwise, it is a number
.