docs›Guides›Expo / React Native Use Aurabase in an Expo / React Native app
Create the client with EXPO_PUBLIC_* variables, keep the session across app restarts, sign users in with a provider through a deep link, and subscribe to database changes.
5 min read·Level intermediate·Revised on Oct 22, 2026
This English text was generated automatically from the French original and has not been reviewed yet.
#Status
1. What was verified
@aurabase/aurabase-js bundles without error for Android in an Expo project (npx expo export --platform android, Expo SDK 58, React Native 0.88). Running the app on a device or a simulator has not been verified yet: treat the session and realtime examples below as the intended usage, and report anything that differs.
Aurabase has no Flutter or Dart package on pub.dev yet: for Flutter, call the REST API (see REST API).
#Setup
2. Install and configure
terminalbash
npx expo install expo-secure-store
npm install @aurabase/aurabase-js
Expo exposes to the app only the variables whose name starts with EXPO_PUBLIC_. Put the project URL and the anon key (never the service_role key: it would ship inside the app) in .env:
.envbash
EXPO_PUBLIC_AURABASE_URL=https://<slug>.aurabase.cloud
EXPO_PUBLIC_AURABASE_ANON_KEY=aura_anon_…
#Session
3. Keep the session
On the web the SDK keeps the session in localStorage; React Native has none. Give the client an auth store that writes the session to expo-secure-store. The store is read at start-up, before the client is created:
lib/aurabase.tstypescript
import * as SecureStore from 'expo-secure-store'
import { BaseAuthStore, createClient } from '@aurabase/aurabase-js'
import type { User } from '@aurabase/aurabase-js'
const KEY = 'aurabase-session'
class SecureStoreAuthStore extends BaseAuthStore {
override save(token: string, refreshToken: string, user: User): void {
super.save(token, refreshToken, user)
void SecureStore.setItemAsync(KEY, JSON.stringify({ token, refreshToken, user }))
}
override clear(): void {
super.clear()
void SecureStore.deleteItemAsync(KEY)
}
}
/** Read the saved session once, at app start, before rendering anything that needs it. */
export async function createAuraClient() {
const store = new SecureStoreAuthStore()
const saved = await SecureStore.getItemAsync(KEY)
if (saved) {
const { token, refreshToken, user } = JSON.parse(saved) as {
token: string
refreshToken: string
user: User
}
store.save(token, refreshToken, user)
}
return createClient(
process.env.EXPO_PUBLIC_AURABASE_URL!,
process.env.EXPO_PUBLIC_AURABASE_ANON_KEY!,
{ auth: { storage: store } },
)
}
Attention
Keep the stored value small. The code of expo-secure-store does not enforce a size limit, but the platform keystores are not designed for large values: if your session carries a lot of user metadata and save fails on a device, keep only the tokens, or use @react-native-async-storage/async-storage (less protected).
#OAuth
4. Sign in with a provider
OAuth in a mobile app uses the system browser and comes back to the app through a deep link. Use the PKCE flow: the code that comes back is useless without the secret the app kept.
- Declare the app scheme in
app.json ("scheme": "myapp"), then add the redirect URL in Studio › Authentication › Configuration › Redirect URLs. In a production build it is myapp://auth-callback; in Expo Go it is the exp://… address that Linking.createURL('auth-callback') prints, so add that one while developing.
- Create the client with
auth: { storage: store, flowType: 'pkce' } (the createClient call above), install the two Expo modules, then start the flow and exchange the code:
terminalbash
npx expo install expo-web-browser expo-linking
lib/signInWithProvider.tstypescript
import * as Linking from 'expo-linking'
import * as WebBrowser from 'expo-web-browser'
import type { AuraClient } from '@aurabase/aurabase-js'
export async function signInWithProvider(aura: AuraClient, provider: 'google' | 'github') {
const redirectTo = Linking.createURL('auth-callback')
// On React Native the SDK does not redirect: it returns the URL to open.
const { data, error } = await aura.auth.signInWithOAuth(provider, { redirectTo })
if (error || !data) return { error }
const result = await WebBrowser.openAuthSessionAsync(data.url, redirectTo)
if (result.type !== 'success') return { error: new Error('Sign-in cancelled') }
// The server redirects to redirectTo?code=… ; exchange the code for a session.
const code = Linking.parse(result.url).queryParams?.code
if (typeof code !== 'string') return { error: new Error('No code in the redirect') }
return aura.auth.exchangeCodeForSession(code)
}
The PKCE secret (code_verifier) stays in memory while the system browser is open. If the system kills the app during sign-in, the exchange returns no_code_verifier: start the flow again.
To sign in with the native Google or Apple dialog instead of a browser, get an ID token from the provider's SDK and call aura.auth.signInWithIdToken('google', idToken). The client ID it is checked against is the one configured for the provider in Studio › Authentication › Providers.
React Native provides a WebSocket, which the SDK uses. A channel opened before the user signs in is re-authenticated when the session changes, so the order of subscribe() and signInWithPassword() does not matter.
components/Todos.tsxtypescript
import { useEffect } from 'react'
import type { AuraClient } from '@aurabase/aurabase-js'
export function useTodoChanges(aura: AuraClient, onChange: (type: string) => void) {
useEffect(() => {
const channel = aura.realtime
.channel('todos')
.on('postgres_changes', { event: '*', schema: 'public', table: 'todos' }, (change) =>
onChange(change.eventType),
)
.subscribe((status) => {
if (status === 'CHANNEL_ERROR') console.warn('realtime refused')
})
return () => {
channel.unsubscribe()
}
}, [aura, onChange])
}
Last updated · Oct 22, 2026