Skip to main content
Bun natively implements a high-performance SQLite3 driver. Import it from the built-in bun:sqlite module — no installation required.
The API is synchronous and modeled after better-sqlite3. bun:sqlite is roughly 3–6x faster than better-sqlite3 for read queries. Features:
  • Prepared statements (cached and uncached)
  • Named and positional parameters
  • Transactions with savepoints
  • BLOBUint8Array type conversion
  • Map rows to class instances with .as(Class)
  • bigint support for 64-bit integers
  • WAL mode for better write concurrency
  • Multi-statement queries via db.run()

Opening a database

Import attribute

Load a database directly via an import statement:

Strict mode

By default, named parameters require a $, :, or @ prefix. With strict: true, you can bind values without prefixes, and missing parameters throw an error:

Closing a database

close(false) is called automatically when the database is garbage collected.

Using the using statement


Queries and statements

db.query() — cached prepared statements

db.query() compiles and caches the SQL statement. Calling it again with the same SQL string returns the cached compiled form, not a new compilation.
Caching applies to the compiled prepared statement, not the query results. The same cached statement can be reused safely with different parameter values.

db.prepare() — uncached prepared statements

Use db.prepare() when you want a fresh Statement that is not stored in the cache (e.g., for dynamically generated SQL):

db.run() — execute without reading results

Use db.run() for INSERT, UPDATE, DELETE, or DDL statements where you don’t need rows back:
Returns { lastInsertRowid: number, changes: number }.

Statement methods

A Statement is a compiled query that can be executed multiple times with different parameter values.

Binding parameters

Named parameters use $, :, or @ prefixes (or no prefix when strict: true):
Positional parameters use ? or ?N:

.get() — fetch first row

.all() — fetch all rows

.run() — execute and get metadata

.values() — fetch rows as arrays

.iterate() — stream rows one at a time

Useful for large result sets that shouldn’t be fully loaded into memory:

.as(Class) — map rows to class instances

The class constructor is not called. Columns are assigned directly to the object and the class prototype is attached. Use this for methods and getters, not constructor initialization.

.toString() — inspect expanded SQL

.finalize() — free resources


Transactions

Wrap multiple queries in an atomic transaction using db.transaction():
If an exception is thrown inside the transaction function, the transaction is automatically rolled back.

Transaction modes

Nested transactions (savepoints)

Calling one transaction function from inside another creates a savepoint:

WAL mode

Write-ahead log mode significantly improves write performance and allows concurrent readers. Enable it at startup:

WAL sidecar file cleanup

WAL mode creates -wal and -shm sidecar files. On macOS (which uses Apple’s SQLite), these persist after db.close(). To clean them up reliably on all platforms:

Integer handling

SQLite supports 64-bit signed integers, but JavaScript numbers only safely represent up to 53 bits. By default, bun:sqlite returns integers as number. Enable safeIntegers to use bigint for large values:
With safeIntegers: true, passing a bigint that exceeds 64 bits throws a RangeError.

Serialization

Serialize and deserialize the entire database to/from a Uint8Array:

Loading extensions

On macOS, Apple’s system SQLite does not support extensions. Install a vanilla SQLite via Homebrew and point bun:sqlite to it:

Type mapping


API reference