React Native guide
Use queries, transactions, extensions, backups, and lifecycle in a mobile application.
These recipes use an open db from the React Native quickstart. Keep database ownership in an application service; components call that service.
Query application data
await db.execute('CREATE TABLE IF NOT EXISTS notes (body text NOT NULL)');
await db.execute('INSERT INTO notes (body) VALUES ($1)', ['First note']);
const result = await db.query('SELECT body FROM notes');
console.log(result.rows);Use $1 parameters for user-supplied values. query returns decoded rows; execute returns command metadata. Use rowMode: 'array' when a query has duplicate column names.
Run a transaction
await db.transaction(async (tx) => {
await tx.execute('INSERT INTO notes (body) VALUES ($1)', ['First']);
await tx.execute('INSERT INTO notes (body) VALUES ($1)', ['Second']);
});Returning commits; throwing rolls back. All callback queries must use tx. Do not send manual transaction-lifecycle SQL or call the outer db. Use tx.rollback() for explicit rollback; savepoints are supported.
Add an extension
Install the native extension package, then select it in the Expo plugin configuration before building:
npm install @oliphaunt/extension-vector@0.3.0{
"expo": {
"plugins": [["@oliphaunt/react-native", { "extensions": ["vector"] }]]
}
}Rebuild the native app, then import its descriptor when opening:
import vector from '@oliphaunt/extension-vector';
const db = await Oliphaunt.open({
storage: { kind: 'applicationData', name: 'main' },
extensions: [vector],
});
await db.execute('CREATE EXTENSION IF NOT EXISTS vector');The plugin packages selected extension resources and dependencies. Extension changes require a native rebuild; a JavaScript update alone cannot add them. For ICU collations, replace the standard seed dependency with @oliphaunt/seed-native-ios-datum64-icu and install @oliphaunt/icu; use matching database-resource versions.
Choose process isolation
The quickstart uses direct mode. For a separate database process, follow mobile broker setup. Broker mode requires its own storage and restore path; use the recipes below for direct mode.
Back up and restore
Direct mode stays bound to one database root and configuration for the lifetime of the application process. Closing a handle does not let that process switch to another root. Prepare the restored data, then use it on the next launch.
const archive = await db.backup();
await db.close();
await Oliphaunt.restore(
{ kind: 'applicationData', name: 'restored' },
archive,
);On a subsequent application launch, open the restored destination:
const restored = await Oliphaunt.open({
storage: { kind: 'applicationData', name: 'restored' },
});
await restored.close();Restore requires new or empty persistent storage. It cannot target temporary storage. Reapply required extensions when opening the restored database. Keep physical restores within a compatible native runtime family.
Handle lifecycle and errors
Call db.cancel() to request interruption of active SQL, and await the query's outcome. Cancelling a JavaScript promise does not interrupt PostgreSQL.
Await db.close() when your application finishes using the database. It stops new work and drains accepted operations. Garbage collection and native module invalidation provide fallback cleanup, but they cannot report shutdown errors to your app.
A SQL failure is a PostgresError with SQLSTATE. Transaction failures roll back when possible; an AggregateError preserves the callback and rollback/database failures when both matter. After a terminal protocol error, close and reopen persistent storage rather than reusing the handle.
Troubleshooting
| Symptom | Check |
|---|---|
| Native module is unavailable | New Architecture enabled and a native build installed |
| Works in development, fails in the app | Runtime resources and extensions are packaged for that platform |
| Changes disappear after restart | Use applicationData or directory, not temporary storage |
| A transaction waits | Use only tx for its SQL |
| Extension changes have no effect | Rebuild the native application |
See Ship a mobile database for backgrounding and recovery guidance.