Domain-Driven Design Supabase client for React Native apps with type-safe operations and singleton pattern.
npm install @umituz/react-native-supabase@supabase/supabase-js>= 2.39.0react>= 18.2.0react-native>= 0.74.0@react-native-async-storage/async-storage>= 1.21.0
- ✅ Domain-Driven Design (DDD) architecture
- ✅ Singleton pattern for single client instance
- ✅ Type-safe Supabase operations
- ✅ React hooks for easy integration
- ✅ Configuration validation
- ✅ Session cache management
- ✅ Security: No .env reading - configuration must be provided by app
- ✅ Works with Expo and React Native CLI
This package does NOT read from .env files for security reasons. You must provide configuration from your application code.
- Security: Prevents accidental exposure of credentials
- Flexibility: Works with any configuration source (Constants, config files, etc.)
- Multi-app support: Same package can be used across hundreds of apps with different configs
Initialize the client early in your app (e.g., in App.tsx or index.js):
import { initializeSupabase } from '@umituz/react-native-supabase';
import Constants from 'expo-constants';
// Get configuration from your app's config source
// Option 1: From Expo Constants (recommended for Expo apps)
const config = {
url: Constants.expoConfig?.extra?.supabaseUrl || process.env.EXPO_PUBLIC_SUPABASE_URL!,
anonKey: Constants.expoConfig?.extra?.supabaseAnonKey || process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY!,
};
// Option 2: From your app's config file
// const config = require('./config/supabase.json');
// Option 3: Direct configuration
// const config = {
// url: 'https://your-project.supabase.co',
// anonKey: 'your-anon-key',
// };
// Initialize
const client = initializeSupabase(config);
if (!client) {
console.error('Failed to initialize Supabase');
// Handle initialization error
}import { getSupabaseClient } from '@umituz/react-native-supabase';
// Get client instance
const client = getSupabaseClient();
// Use Supabase features
const { data, error } = await client.from('users').select();
const { data: authData } = await client.auth.signInWithPassword({
email: 'user@example.com',
password: 'password',
});import { useSupabase } from '@umituz/react-native-supabase';
const MyComponent = () => {
const { client, isInitialized, getClientSafely } = useSupabase();
if (!isInitialized) {
return <Text>Loading...</Text>;
}
const fetchData = async () => {
const { data, error } = await client.from('users').select();
if (error) {
console.error(error);
return;
}
console.log(data);
};
return (
<View>
<Button title="Fetch Data" onPress={fetchData} />
</View>
);
};import { initializeSupabase } from '@umituz/react-native-supabase';
import AsyncStorage from '@react-native-async-storage/async-storage';
const config = {
url: 'https://your-project.supabase.co',
anonKey: 'your-anon-key',
// Optional: Custom storage adapter
storage: {
getItem: (key: string) => AsyncStorage.getItem(key),
setItem: (key: string, value: string) => AsyncStorage.setItem(key, value),
removeItem: (key: string) => AsyncStorage.removeItem(key),
},
// Optional: Custom schema
schema: 'public',
// Optional: Realtime heartbeat interval
realtimeHeartbeatIntervalMs: 30000,
// Optional: Auth settings
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: false,
};
initializeSupabase(config);import { clearSupabaseSessionCache } from '@umituz/react-native-supabase';
// Clear session cache (useful for logout and GDPR compliance)
await clearSupabaseSessionCache();import {
getSupabaseClient,
SupabaseInitializationError,
SupabaseConfigurationError,
} from '@umituz/react-native-supabase';
try {
const client = getSupabaseClient();
// Use client
} catch (error) {
if (error instanceof SupabaseInitializationError) {
console.error('Supabase not initialized:', error.message);
} else if (error instanceof SupabaseConfigurationError) {
console.error('Invalid configuration:', error.message);
}
}import {
isSupabaseInitialized,
getSupabaseInitializationError,
} from '@umituz/react-native-supabase';
if (isSupabaseInitialized()) {
console.log('Supabase is ready');
} else {
const error = getSupabaseInitializationError();
console.error('Initialization error:', error);
}initializeSupabase(config): Initialize Supabase client with configurationgetSupabaseClient(): Get Supabase client instance (throws if not initialized)isSupabaseInitialized(): Check if client is initializedgetSupabaseInitializationError(): Get initialization error if anyclearSupabaseSessionCache(): Clear all session cacheresetSupabaseClient(): Reset client instance (useful for testing)
useSupabase(): React hook for accessing Supabase client
SupabaseConfig: Configuration interfaceSupabaseClientType: Supabase client typeUseSupabaseResult: Hook return type
SupabaseError: Base error classSupabaseInitializationError: Initialization errorsSupabaseConfigurationError: Configuration errorsSupabaseAuthError: Authentication errorsSupabaseDatabaseError: Database errorsSupabaseStorageError: Storage errorsSupabaseRealtimeError: Realtime errors
For Expo apps, configure in app.config.js:
module.exports = () => {
return {
expo: {
// ... other config
extra: {
supabaseUrl: process.env.EXPO_PUBLIC_SUPABASE_URL,
supabaseAnonKey: process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY,
},
},
};
};Then initialize in your app:
import { initializeSupabase } from '@umituz/react-native-supabase';
import Constants from 'expo-constants';
const config = {
url: Constants.expoConfig?.extra?.supabaseUrl!,
anonKey: Constants.expoConfig?.extra?.supabaseAnonKey!,
};
initializeSupabase(config);- Never commit credentials: Use environment variables or secure config files
- Use anon key only: This package only requires the anonymous/public key (safe for client-side)
- Implement RLS: Use Supabase Row Level Security for data protection
- Clear sessions on logout: Always call
clearSupabaseSessionCache()on logout
MIT