Concepts
Read this before writing application code — it explains the shapes and rules that every guide and API page assumes.
Every call resolves, never rejects
Every plugin method returns a Promise that resolves to a SqliteResult<T> — a discriminated
union of SqliteSuccess<T> ({ success: true, data: T }) and SqliteFailure
({ success: false, error: SqliteError }). SQL failures, validation errors, and platform errors
all surface through success: false, not through a rejected Promise.
const result = await CapacitorSqlite.run({ database: 'myapp', statement: 'INSERT ...' });
if (!result.success) {
console.error(result.error.code, result.error.message);
return;
}
console.log(result.data.changes);
This matters most inside try/catch blocks around transactions: a failed run() or
commitTransaction() does not throw, so try/catch alone does not trigger a rollback. See
Transactions.
SqliteError.details always includes nativeCode, nativeMessage, and source; treat any
other keys as platform-specific debugging hints, not part of the stable contract.
Databases are opened by name, not by path
open({ database: 'myapp' }) takes a database name (no extension), not a filesystem path. The
plugin resolves an actual file location per platform and per directory option — see
Platform notes for the resulting paths. Passing a raw absolute or relative
path is not supported.
On iOS and Electron, the open-database registry matches names case-insensitively, so 'myapp'
and 'MyApp' resolve to the same open handle and cannot both be open with different settings at
once.
Reopening an already-open database
Calling open() again for a database that is already open reuses the existing connection
when readonly and directory match the original call. If migrations are supplied on the
second call, any still-pending versions are applied before it resolves; already-applied versions
are skipped. Reopening with a different readonly value or directory returns
DB_ALREADY_OPEN. Reopening with migrations while a manual transaction is active on that
connection returns MIGRATION_FAILED, so migration SQL never mixes into an in-flight
application transaction.
Migrations run against PRAGMA user_version
open() reads the database's PRAGMA user_version, then runs every supplied migration whose
version exceeds that stored value, ascending. Each migration commits in its own transaction.
Details, ordering guarantees, and the non-atomicity of a multi-version list are covered in
Migrations.
Transactions are connection-scoped
beginTransaction() / commitTransaction() / rollbackTransaction() operate on one database's
connection. A second beginTransaction() while one is already active — or calling execute() /
runBatch() with their default transaction: true while a manual transaction is active —
returns TRANSACTION_FAILED. close() automatically rolls back any open transaction. See
Transactions.
In-memory databases
':memory:' as the database name opens an ephemeral database — useful for tests or scratch
space. The directory option is ignored for it. On iOS, Web, and Electron, close() destroys
the database outright, so a later open({ database: ':memory:' }) always starts empty. On
Android, SQLiteDatabase's connection pool has been observed to keep a :memory: database's
contents alive across a close/reopen cycle (tracked as mdb-02 in the shared test suite); run
explicit cleanup SQL (DROP TABLE, etc.) if your code depends on a guaranteed reset there.
Calls against one database are serialized
On Android and iOS, every plugin call — across all open databases — is funneled through a single native SQLite thread/serial queue per platform (one executor thread on Android, one serial work queue in front of each database's connection queue on iOS), so manual transactions stay affinity-correct on the same native thread. Web and Electron each use one underlying SQLite worker per plugin instance, with a separate logical FIFO per database.
The practical effect: always await transaction boundaries before issuing dependent work, and a
long operation on one database can delay calls to a different database that share the same
native thread/worker. Apps that keep only one database open are unaffected by the multi-database
case. See Platform notes for the full caveat list.
Value types
| JS type | SQLite affinity |
|---|---|
string |
TEXT |
number |
INTEGER / REAL |
boolean |
INTEGER (0 / 1) |
null |
NULL |
Uint8Array |
BLOB |
number values must be finite, and integer values must stay within
Number.MAX_SAFE_INTEGER. An INTEGER result outside that safe range comes back as a string
instead of an imprecise number. See Usage for BLOB
transport details.
Next steps
- Usage — CRUD basics, placeholders, and value binding.
- Migrations — versioning rules and failure semantics.
- Transactions — manual transaction lifecycle.
- Bulk writes —
runBatch()andrunMany().