This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
Expo SQLite
一个通过 SQLite API 提供可查询数据库访问的库。
expo-sqlite 让你的应用能够访问一个可通过 SQLite API 查询的数据库。该数据库会在应用重启后持续保留。
警告: 在 Apple TV 上,底层数据库文件位于缓存目录,而不是应用的 documents 目录,详见 Apple 平台指南。
安装
If you are installing this in an existing React Native app, make sure to install expo in your project.
在 app 配置中进行配置
如果你的项目使用了配置插件(Continuous Native Generation(CNG)),你可以使用内置的 配置插件 为 expo-sqlite 配置高级选项。该插件允许你配置一些无法在运行时设置、且需要重新构建新的应用二进制文件后才会生效的属性。如果你的应用不使用 CNG,那么你需要手动配置该库。
Example app.json with config plugin
Configurable properties
Web 设置
重要: Web 支持处于 alpha 阶段,可能不稳定。如果你遇到任何问题,请在 GitHub 上创建 issue。
要在 Web 上使用 expo-sqlite,你需要配置 Metro 打包器以支持 wasm 文件,并添加 HTTP 标头以允许使用 SharedArrayBuffer。
将以下配置添加到你的 metro.config.js 中。如果你还没有 metro.config.js,可以运行 npx expo customize metro.config.js。了解更多。
如果你将应用部署到 Web 托管服务,还需要在 Web 服务器上添加 Cross-Origin-Embedder-Policy 和 Cross-Origin-Opener-Policy 标头。了解 COEP、COOP 标头以及 SharedArrayBuffer 的更多信息。
使用
从 expo-sqlite 导入模块。
基本 CRUD 操作
预编译语句
预编译语句允许你只编译一次 SQL 查询,然后使用不同参数多次执行。它们会自动转义输入参数以防御 SQL 注入攻击,因此推荐用于包含用户输入的查询。你可以通过在数据库实例上调用 prepareAsync() 或 prepareSync() 方法来获取预编译语句。预编译语句可以通过调用 executeAsync() 或 executeSync() 方法来完成 CRUD 操作。
注意: 在使用完语句后,记得调用
finalizeAsync()或finalizeSync()方法来释放预编译语句。建议使用try-finally代码块来确保预编译语句被最终释放。
useSQLiteContext() 钩子
带有 React.Suspense 的 useSQLiteContext() 钩子
与 useSQLiteContext() 钩子一样,你也可以将 SQLiteProvider 与 React.Suspense 集成,在数据库准备好之前显示一个 fallback 组件。要启用该集成,请向 SQLiteProvider 组件传入 useSuspense 属性。
在异步事务中执行查询
由于 async/await 的特性,任何在事务处于活动状态时运行的查询都会被包含在事务中。这也包括传递给 withTransactionAsync() 的作用域函数之外的查询语句,这可能会让人感到意外。例如,下面的测试用例会在传递给 withTransactionAsync() 的作用域函数内部和外部运行查询。然而,所有查询都会在实际的 SQL 事务中运行,因为第二个 UPDATE 查询是在事务结束之前执行的。
Promise.all([ // 1. 一个新的事务开始 db.withTransactionAsync(async () => { // 2. 值 "first" 被插入到测试表中,然后我们等待 2 // 秒 await db.execAsync('INSERT INTO test (data) VALUES ("first")'); await sleep(2000); // 4. 两秒后,我们从表中读取最新数据 const row = await db.getFirstAsync<{ data: string }>('SELECT data FROM test'); // ❌ 表中的数据将是 "second",这个断言会失败。 // 此外,这个断言还会抛出错误并回滚事务,包括下面的 `UPDATE` 查询,因为它是在事务中执行的。 expect(row.data).toBe('first'); }), // 3. 一秒后,测试表中的数据被更新为 "second"。 // 这个 `UPDATE` 查询虽然代码在事务外部,但由于执行时事务恰好处于活动状态,因此它仍会在事务中运行。 sleep(1000).then(async () => db.execAsync('UPDATE test SET data = "second"')), ]);
withExclusiveTransactionAsync() 函数可以解决这个问题。只有在传递给 withExclusiveTransactionAsync() 的作用域函数内部运行的查询,才会在实际的 SQL 事务中执行。
执行 PRAGMA 查询
信息:提示: 在创建新数据库时启用 WAL journal mode,通常可以提升性能。
导入现有数据库
要使用你已有的 .db 文件打开一个新的 SQLite 数据库,你可以使用 SQLiteProvider 和 assetSource。
在应用/扩展之间共享数据库(iOS)
要在同一个 App Group 中与其他应用/扩展共享数据库,你可以按照以下步骤使用共享容器:
1
在 app 配置中配置 App Group:
2
使用 expo-file-system 库中的 Paths.appleSharedContainers 来获取共享容器的路径:
传递二进制数据
使用 Uint8Array 向数据库传递二进制数据:
浏览设备上的数据库
你可以使用 drizzle-studio-expo 开发工具插件 来检查数据库、对其执行查询并浏览数据。这个插件可以让你直接从 Expo CLI 启动 Drizzle Studio,并连接到你应用中的数据库。该插件可用于任何 expo-sqlite 配置。它不要求你在应用中使用 Drizzle ORM。了解如何安装和使用该插件。
键值存储
expo-sqlite 库提供了 Storage,可作为 @react-native-async-storage/async-storage 库的直接替代。这个键值存储由 SQLite 支持。如果你的项目已经在使用 expo-sqlite,你可以直接使用 expo-sqlite/kv-store,而无需再添加另一个依赖。
Storage 提供与 @react-native-async-storage/async-storage 相同的 API:
使用 expo-sqlite/kv-store 的一个主要优点是还新增了同步 API,使用起来更加方便:
如果你当前在项目中使用的是 @react-native-async-storage/async-storage,切换到 expo-sqlite/kv-store 只需修改导入语句即可:
- import AsyncStorage from '@react-native-async-storage/async-storage'; + import AsyncStorage from 'expo-sqlite/kv-store';
localStorage API
expo-sqlite/localStorage/install 模块提供了 localStorage API 的直接实现。如果你已经熟悉网页端的这个 API,或者希望在 Web 和其他平台之间共享存储代码,这会很有用。要使用它,你只需要导入 expo-sqlite/localStorage/install 模块:
注意:
import 'expo-sqlite/localStorage/install';在 Web 上不会执行任何操作,并且会被排除在生产环境的 JS 包之外。
安全
SQL 注入是一类漏洞,攻击者会诱使你的应用将用户输入当作 SQL 代码执行。你必须对传递给 SQLite 的所有用户输入进行转义,以防御 SQL 注入。预处理语句是应对这一问题的有效防御手段。它们明确地将 SQL 查询的逻辑与其输入参数分离,而 SQLite 在执行预处理语句时会自动对输入进行转义。
第三方库集成
expo-sqlite 库旨在成为一个稳固的 SQLite 基础层。它支持与第三方库进行更广泛的集成,以实现更高级的上层功能。以下是一些可与 expo-sqlite 一起使用的库。
Drizzle ORM
Drizzle 是一个 “无头、类型安全的 TypeScript ORM”。它可运行于 Node.js、Bun、Deno 和 React Native。它还有一个名为 drizzle-kit 的 CLI 配套工具,用于生成 SQL 迁移。
查看更多详情,请参阅 Drizzle ORM 文档 和 expo-sqlite 集成指南。
Knex.js
Knex.js 是一个 SQL 查询构建器,具有“灵活、可移植且使用起来很有趣!” 的特点。
查看更多详情,请参阅 expo-sqlite 集成指南。
SQLCipher
注意: Expo Go 不支持 SQLCipher。
SQLCipher 是 SQLite 的一个分支,为数据库增加了加密和身份验证功能。expo-sqlite 库支持 Android、iOS 和 macOS 上的 SQLCipher。要使用 SQLCipher,你需要按照 app 配置中的配置 部分所示,将 useSQLCipher 配置添加到你的 app.json 中,然后运行 npx expo prebuild。
在你打开数据库之后,需要使用 PRAGMA key = 'password' 语句为数据库设置密码。
API
常用 API 速查表
下表总结了 SQLiteDatabase 和 SQLiteStatement 类的常用 API:
Component
Type: React.Element<SQLiteProviderProps>
Context.Provider component that provides a SQLite database to all children.
All descendants of this component will be able to access the database using the useSQLiteContext hook.
SQLiteProviderAssetSourceImport a bundled database file from the specified asset module.
Example
assetSource={{ assetId: require('./assets/db.db') }}
string • Default: defaultDatabaseDirectoryThe directory where the database file is located.
(error: Error) => void • Default: rethrow the errorHandle errors from SQLiteProvider.
(db: SQLiteDatabase) => Promise<void>A custom initialization handler to run before rendering the children. You can use this to run database migrations or other setup tasks.
boolean • Default: falseEnable React.Suspense integration.
Example
export default function App() { return ( <Suspense fallback={<Text>Loading...</Text>}> <SQLiteProvider databaseName="test.db" useSuspense={true}> <Main /> </SQLiteProvider> </Suspense> ); }
Constants
Type: SQLiteStorage
This default instance of the SQLiteStorage class is used as a drop-in replacement for the AsyncStorage module from @react-native-async-storage/async-storage.
Type: Record<string, {
entryPoint: string,
libPath: string
} | undefined>
The pre-bundled SQLite extensions.
Type: any
The default directory for SQLite databases.
Type: SQLiteStorage
Alias for AsyncStorage, given the storage not only offers asynchronous methods.
Hooks
A global hook for accessing the SQLite database across components.
This hook should only be used within a <SQLiteProvider> component.
SQLiteDatabaseExample
export default function App() { return ( <SQLiteProvider databaseName="test.db"> <Main /> </SQLiteProvider> ); } export function Main() { const db = useSQLiteContext(); console.log('sqlite version', db.getFirstSync('SELECT sqlite_version()')); return <View /> }
Classes
A SQLite database.
SQLiteDatabase Properties
NativeDatabaseSQLiteOpenOptionsSQLiteDatabase Methods
Close the database.
Promise<void>Create a new session for the database.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
SQLiteSessionExecute all SQL queries in the supplied string.
Note: The queries are not escaped for you! Be careful when constructing your queries.
Promise<void>Execute all SQL queries in the supplied string.
Note: The queries are not escaped for you! Be careful when constructing your queries.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
voidA convenience wrapper around SQLiteDatabase.prepareAsync(), SQLiteStatement.executeAsync(), SQLiteExecuteAsyncResult.getAllAsync(), and SQLiteStatement.finalizeAsync().
Promise<T[]>Example
// For unnamed parameters, you pass values in an array. db.getAllAsync('SELECT * FROM test WHERE intValue = ? AND name = ?', [1, 'Hello']); // For unnamed parameters, you pass values in variadic arguments. db.getAllAsync('SELECT * FROM test WHERE intValue = ? AND name = ?', 1, 'Hello'); // For named parameters, you should pass values in object. db.getAllAsync('SELECT * FROM test WHERE intValue = $intValue AND name = $name', { $intValue: 1, $name: 'Hello' });
A convenience wrapper around SQLiteDatabase.prepareSync(), SQLiteStatement.executeSync(), SQLiteExecuteSyncResult.getAllSync(), and SQLiteStatement.finalizeSync().
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
T[]A convenience wrapper around SQLiteDatabase.prepareAsync(), SQLiteStatement.executeAsync(), SQLiteExecuteAsyncResult AsyncIterator, and SQLiteStatement.finalizeAsync().
AsyncIterableIterator<T>Rather than returning Promise, this function returns an AsyncIterableIterator. You can use for await...of to iterate over the rows from the SQLite query result.
A convenience wrapper around SQLiteDatabase.prepareSync(), SQLiteStatement.executeSync(), SQLiteExecuteSyncResult Iterator, and SQLiteStatement.finalizeSync().
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
IterableIterator<T>This function returns an IterableIterator. You can use for...of to iterate over the rows from the SQLite query result.
A convenience wrapper around SQLiteDatabase.prepareAsync(), SQLiteStatement.executeAsync(), SQLiteExecuteAsyncResult.getFirstAsync(), and SQLiteStatement.finalizeAsync().
Promise<null | T>A convenience wrapper around SQLiteDatabase.prepareSync(), SQLiteStatement.executeSync(), SQLiteExecuteSyncResult.getFirstSync(), and SQLiteStatement.finalizeSync().
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
null | TAsynchronous call to return whether the database is currently in a transaction.
Promise<boolean>Synchronous call to return whether the database is currently in a transaction.
booleanLoad a SQLite extension.
Promise<void>Example
// Load `sqlite-vec` from `bundledExtensions`. You need to enable `withSQLiteVecExtension` to include `sqlite-vec`. const extension = SQLite.bundledExtensions['sqlite-vec']; await db.loadExtensionAsync(extension.libPath, extension.entryPoint); // You can also load a custom extension. await db.loadExtensionAsync('/path/to/extension');
Load a SQLite extension.
voidExample
// Load `sqlite-vec` from `bundledExtensions`. You need to enable `withSQLiteVecExtension` to include `sqlite-vec`. const extension = SQLite.bundledExtensions['sqlite-vec']; db.loadExtensionSync(extension.libPath, extension.entryPoint); // You can also load a custom extension. db.loadExtensionSync('/path/to/extension');
Create a prepared SQLite statement.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
SQLiteStatementA convenience wrapper around SQLiteDatabase.prepareAsync(), SQLiteStatement.executeAsync(), and SQLiteStatement.finalizeAsync().
Promise<SQLiteRunResult>A convenience wrapper around SQLiteDatabase.prepareSync(), SQLiteStatement.executeSync(), and SQLiteStatement.finalizeSync().
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
SQLiteRunResultSerialize the database as Uint8Array.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
Uint8ArraySynchronize the local database with the remote libSQL server. This method is only available from libSQL integration.
Promise<void>Execute a transaction and automatically commit/rollback based on the task result.
The transaction may be exclusive.
As long as the transaction is converted into a write transaction,
the other async write queries will abort with database is locked error.
Note: This function is not supported on web.
Promise<void>Example
db.withExclusiveTransactionAsync(async (txn) => { await txn.execAsync('UPDATE test SET name = "aaa"'); });
Execute a transaction and automatically commit/rollback based on the task result.
Note: This transaction is not exclusive and can be interrupted by other async queries.
Promise<void>Example
db.withTransactionAsync(async () => { await db.execAsync('UPDATE test SET name = "aaa"'); // // We cannot control the order of async/await order, so order of execution is not guaranteed. // The following UPDATE query out of transaction may be executed here and break the expectation. // const result = await db.getFirstAsync<{ name: string }>('SELECT name FROM Users'); expect(result?.name).toBe('aaa'); }); db.execAsync('UPDATE test SET name = "bbb"');
If you worry about the order of execution, use withExclusiveTransactionAsync instead.
A class that represents an instance of the SQLite session extension.
See: Session Extension
SQLiteSession Methods
Apply a changeset synchronously.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
voidAttach a table to the session synchronously.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
voidClose the session synchronously.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
voidCreate a changeset synchronously.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
ChangesetCreate an inverted changeset asynchronously.
This is a shorthand for createChangesetAsync() + invertChangesetAsync().
Create an inverted changeset synchronously.
This is a shorthand for createChangesetSync() + invertChangesetSync().
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
ChangesetEnable or disable the session synchronously.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
voidInvert a changeset synchronously.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
ChangesetA prepared statement returned by SQLiteDatabase.prepareAsync() or SQLiteDatabase.prepareSync() that can be binded with parameters and executed.
SQLiteStatement Methods
Run the prepared statement and return the SQLiteExecuteAsyncResult instance.
Promise<SQLiteExecuteAsyncResult<T>>Run the prepared statement and return the SQLiteExecuteSyncResult instance.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
SQLiteExecuteSyncResult<T>Finalize the prepared statement. This will call the sqlite3_finalize() C function under the hood.
Attempting to access a finalized statement will result in an error.
Note: While
expo-sqlitewill automatically finalize any orphaned prepared statements upon closing the database, it is considered best practice to manually finalize prepared statements as soon as they are no longer needed. This helps to prevent resource leaks. You can use thetry...finallystatement to ensure that prepared statements are finalized even if an error occurs.
Promise<void>Finalize the prepared statement. This will call the sqlite3_finalize() C function under the hood.
Attempting to access a finalized statement will result in an error.
Note: While
expo-sqlitewill automatically finalize any orphaned prepared statements upon closing the database, it is considered best practice to manually finalize prepared statements as soon as they are no longer needed. This helps to prevent resource leaks. You can use thetry...finallystatement to ensure that prepared statements are finalized even if an error occurs.
voidGet the column names of the prepared statement.
Promise<string[]>Key-value store backed by SQLite. This class accepts a databaseName parameter in its constructor, which is the name of the database file to use for the storage.
SQLiteStorage Methods
Alias for clearAsync() method.
Promise<void>Clears all key-value pairs from the storage asynchronously.
Promise<boolean>Clears all key-value pairs from the storage synchronously.
booleanAlias for closeAsync() method.
Promise<void>Closes the database connection asynchronously.
Promise<void>Alias for getAllKeysAsync() method.
Promise<string[]>Retrieves all keys stored in the storage asynchronously.
Promise<string[]>Retrieves all keys stored in the storage synchronously.
string[]Retrieves the value associated with the given key synchronously.
null | stringRetrieves the key at the given index synchronously.
null | stringRetrieves the number of key-value pairs stored in the storage asynchronously.
Promise<number>Retrieves the number of key-value pairs stored in the storage synchronously.
numberMerges the given value with the existing value for the given key asynchronously. If the existing value is a JSON object, performs a deep merge.
Promise<void>Merges multiple key-value pairs asynchronously. If existing values are JSON objects, performs a deep merge.
Promise<void>Removes the value associated with the given key synchronously.
booleanSets the value for the given key asynchronously. If a function is provided, it computes the new value based on the previous value.
Promise<void>Methods
Backup a database to another database.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
voidCompares two objects deeply for equality.
booleanDelete a database file.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
voidGiven a Uint8Array data and deserialize to memory database.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
SQLiteDatabaseOpen a database.
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
SQLiteDatabaseEvent subscriptions
Add a listener for database changes.
Note: to enable this feature, you must set
enableChangeListenertotruewhen opening the database.
EventSubscriptionA Subscription object that you can call remove() on when you would like to unsubscribe the listener.
Interfaces
Extends: AsyncIterableIterator<T>
A result returned by SQLiteStatement.executeAsync().
Example
The result includes the lastInsertRowId and changes properties. You can get the information from the write operations.
const statement = await db.prepareAsync('INSERT INTO test (value) VALUES (?)'); try { const result = await statement.executeAsync(101); console.log('lastInsertRowId:', result.lastInsertRowId); console.log('changes:', result.changes); } finally { await statement.finalizeAsync(); }
Example
The result implements the AsyncIterator interface, so you can use it in for await...of loops.
const statement = await db.prepareAsync('SELECT value FROM test WHERE value > ?'); try { const result = await statement.executeAsync<{ value: number }>(100); for await (const row of result) { console.log('row value:', row.value); } } finally { await statement.finalizeAsync(); }
Example
If your write operations also return values, you can mix all of them together.
const statement = await db.prepareAsync('INSERT INTO test (name, value) VALUES (?, ?) RETURNING name'); try { const result = await statement.executeAsync<{ name: string }>('John Doe', 101); console.log('lastInsertRowId:', result.lastInsertRowId); console.log('changes:', result.changes); for await (const row of result) { console.log('name:', row.name); } } finally { await statement.finalizeAsync(); }
SQLiteExecuteAsyncResult Methods
Get all rows of the result set. This requires the SQLite cursor to be in its initial state. If you have already retrieved rows from the result set, you need to reset the cursor first by calling resetAsync(). Otherwise, an error will be thrown.
Promise<T[]>Get the first row of the result set. This requires the SQLite cursor to be in its initial state. If you have already retrieved rows from the result set, you need to reset the cursor first by calling resetAsync(). Otherwise, an error will be thrown.
Promise<null | T>Reset the prepared statement cursor. This will call the sqlite3_reset() C function under the hood.
Promise<void>Extends: IterableIterator<T>
A result returned by SQLiteStatement.executeSync().
Note: Running heavy tasks with this function can block the JavaScript thread and affect performance.
Example
The result includes the lastInsertRowId and changes properties. You can get the information from the write operations.
const statement = db.prepareSync('INSERT INTO test (value) VALUES (?)'); try { const result = statement.executeSync(101); console.log('lastInsertRowId:', result.lastInsertRowId); console.log('changes:', result.changes); } finally { statement.finalizeSync(); }
Example
The result implements the Iterator interface, so you can use it in for...of loops.
const statement = db.prepareSync('SELECT value FROM test WHERE value > ?'); try { const result = statement.executeSync<{ value: number }>(100); for (const row of result) { console.log('row value:', row.value); } } finally { statement.finalizeSync(); }
Example
If your write operations also return values, you can mix all of them together.
const statement = db.prepareSync('INSERT INTO test (name, value) VALUES (?, ?) RETURNING name'); try { const result = statement.executeSync<{ name: string }>('John Doe', 101); console.log('lastInsertRowId:', result.lastInsertRowId); console.log('changes:', result.changes); for (const row of result) { console.log('name:', row.name); } } finally { statement.finalizeSync(); }
SQLiteExecuteSyncResult Methods
Get all rows of the result set. This requires the SQLite cursor to be in its initial state. If you have already retrieved rows from the result set, you need to reset the cursor first by calling resetSync(). Otherwise, an error will be thrown.
T[]Get the first row of the result set. This requires the SQLite cursor to be in its initial state. If you have already retrieved rows from the result set, you need to reset the cursor first by calling resetSync(). Otherwise, an error will be thrown.
null | TReset the prepared statement cursor. This will call the sqlite3_reset() C function under the hood.
voidA result returned by SQLiteDatabase.runAsync or SQLiteDatabase.runSync.
Types
The event payload for the listener of addDatabaseChangeListener
Literal type: Record
Acceptable values are: Record<string, SQLiteBindValue>
Literal type: union
Bind parameters to the prepared statement. You can either pass the parameters in the following forms:
Example
A single array for unnamed parameters.
const statement = await db.prepareAsync('SELECT * FROM test WHERE value = ? AND intValue = ?'); const result = await statement.executeAsync(['test1', 789]); const firstRow = await result.getFirstAsync();
Example
Variadic arguments for unnamed parameters.
const statement = await db.prepareAsync('SELECT * FROM test WHERE value = ? AND intValue = ?'); const result = await statement.executeAsync('test1', 789); const firstRow = await result.getFirstAsync();
Example
A single object for named parameters
We support multiple named parameter forms such as :VVV, @VVV, and $VVV. We recommend using $VVV because JavaScript allows using $ in identifiers without escaping.
const statement = await db.prepareAsync('SELECT * FROM test WHERE value = $value AND intValue = $intValue'); const result = await statement.executeAsync({ $value: 'test1', $intValue: 789 }); const firstRow = await result.getFirstAsync();
Acceptable values are: string | number | null | boolean | Uint8Array
Update function for the setItemAsync() or setItemSync() method. It computes the new value based on the previous value. The function returns the new value to set for the key.
string
Type: SQLiteBindValue[]