NitroSQLite
Guides

Sync and async

Pick the query form that fits your workload and understand connection ordering.

Use db.execute() for a small query that needs an immediate result, and await db.executeAsync() for work that may take longer. A synchronous call blocks its JavaScript caller until SQLite finishes. An asynchronous call returns a promise and lets JavaScript continue while the database work runs in the background.

executeBatch and loadFile also have matching Async methods. A prepared statement has execute() and executeAsync() for repeated SQL.

import { open } from 'react-native-nitro-sqlite'

const db = open({ name: 'app.sqlite' })
db.execute('CREATE TABLE IF NOT EXISTS notes (body TEXT NOT NULL)')

const smallResult = db.execute('SELECT 1 AS value')
const largerResult = await db.executeAsync('SELECT * FROM notes')

console.log(smallResult.results, largerResult.results)

Use synchronous calls when immediate results are useful and the work is small. Prefer async calls for queries that may scan many rows, batches, or file imports. Performance guidance covers other ways to keep work bounded.

executeAsync() resolves once with the complete result in row order. Returning many rows still costs JavaScript work, so paginate large results.

Connection ordering

Calls on one open() connection run one at a time in submission order. Await a write before a read that depends on it so you can handle write errors. Batches, transactions, and prepared statement executions wait for preceding work and reserve the connection until complete. A synchronous operation, close(), or delete() throws a busy error if work is pending or active. Await async work before calling a synchronous method on that connection:

await db.executeAsync('INSERT INTO notes (body) VALUES (?)', ['Queued write'])
const total = db
  .execute<{ total: number }>('SELECT COUNT(*) AS total FROM notes')
  .rows.item(0)?.total
console.log(total)

Inside db.transaction(), use the callback's tx methods. Calling and awaiting another async operation on the same db leaves it waiting for the callback to finish. See transactions for that restriction and multiple connections for work that can run independently.

See prepared statements for statement cleanup.

Global helpers and native access

The NitroSQLite export also has execute, executeAsync, prepare, executeBatch, executeBatchAsync, and transaction helpers that take a database name. NitroSQLite.open(options) is the same helper as the named open() export. Global queries join the default connection's JavaScript queue when the name was opened through open(). They do not address independent connections; use the object returned by open() for those. Without a default managed connection, global execute and executeAsync call the native methods directly and still need an open native database handle. Global prepare, batches, and transactions need a default connection opened through open().

NitroSQLite.native exposes the underlying NitroSQLiteNative hybrid object. Its methods take a database name and return raw results without the connection helper's rows container. They also throw native errors without converting them to NitroSQLiteError. Its executeAsync() accepts an optional onRows callback that receives leading rows in batches, in order; the resolved results then holds only the rows after the last batch.

import { NitroSQLite } from 'react-native-nitro-sqlite'

NitroSQLite.native.open('raw.sqlite')
try {
  const result = NitroSQLite.native.execute('raw.sqlite', 'SELECT 1 AS value')
  console.log(result.results[0]?.value)
} finally {
  NitroSQLite.native.close('raw.sqlite')
}

Native calls bypass the JavaScript queue. If you use them alongside an open() connection, coordinate access yourself. A native statement can run inside an active connection transaction without joining its callback's sequence. The raw result is a plain JavaScript object with rowsAffected, insertId, results, and metadata; ordinary garbage collection reclaims it. NitroSQLite.native itself is a Nitro hybrid object with a dispose() member. Do not dispose that shared instance during normal database cleanup; dispose() is not a database close() call.

Call raw methods through NitroSQLite.native. Keep the shared threadSafe setting enabled when work can run across multiple database connections.

On this page