React Native SDK
The Calljmp React Native SDK provides a comprehensive solution for mobile backend development, featuring secure authentication, direct SQLite database access, file storage, and custom service integration. Built with security-first principles, it uses App Attestation (iOS) and Play Integrity (Android) instead of traditional API keys.
Key Features
- Secure Authentication - Email/password authentication with user tagging
- Direct Database Access - Full SQLite database access with parameterized queries
- File Storage - Secure cloud storage with bucket organization
- Device Attestation - App Attestation (iOS) and Play Integrity (Android)
- Custom Services - Integration with custom backend services
- Project Management - Multi-project support with connection management
Installation
Install the Calljmp SDK in your React Native project:
npm install @calljmp/react-native
Platform Setup
For iOS, navigate to the ios
directory and install pods:
cd ios && pod install && cd ..
For React Native versions below 0.60, manually link the SDK:
react-native link @calljmp/react-native
Quick Start
Initialize the SDK
Import and initialize Calljmp in your React Native app:
App.tsx
import { Calljmp } from '@calljmp/react-native'
const calljmp = new Calljmp({
android: {
cloudProjectNumber: GOOGLE_CLOUD_PROJECT_NUMBER,
},
// Optional: Development configuration
development: {
enabled: __DEV__,
apiToken: CALLJMP_DEV_API_TOKEN,
},
})
Authentication
Calljmp provides secure email/password authentication with user tagging and flexible policies.
Email Authentication
Authenticate users with email and password:
import { UserAuthenticationPolicy } from '@calljmp/react-native'
const result = await calljmp.users.auth.email.authenticate({
email: 'user@example.com',
password: 'securePassword123',
policy: UserAuthenticationPolicy.SignInOrCreate,
tags: ['role:user', 'department:sales'],
})
if (result.error) {
console.error('Authentication failed:', result.error)
return
}
const user = result.data.user
console.log('User authenticated:', user.email)
console.log('User tags:', user.tags)
Authentication Policies
Control authentication behavior with different policies:
// Only create new accounts, fail if account exists
await calljmp.users.auth.email.authenticate({
email: 'newuser@example.com',
password: 'password',
policy: UserAuthenticationPolicy.CreateNewOnly,
})
// Only sign into existing accounts, fail if account doesn't exist
await calljmp.users.auth.email.authenticate({
email: 'existinguser@example.com',
password: 'password',
policy: UserAuthenticationPolicy.SignInExistingOnly,
})
// Try to sign in, create account if it doesn't exist (default)
await calljmp.users.auth.email.authenticate({
email: 'user@example.com',
password: 'password',
policy: UserAuthenticationPolicy.SignInOrCreate,
})
Database Operations
Calljmp provides direct SQLite database access with full SQL support and row-level security.
Basic Queries
Execute SQL queries directly:
// Simple SELECT query
const result = await calljmp.database.query({
sql: 'SELECT id, email, tags, created_at FROM users',
params: [],
})
if (result.error) {
console.error('Query failed:', result.error)
return
}
console.log(`Found ${result.data.results.length} users`)
result.data.results.forEach(row => {
console.log(`User: ${row.email} (${row.tags})`)
})
Parameterized Queries
Use parameterized queries for security:
// Query with parameters
const result = await calljmp.database.query({
sql: 'SELECT * FROM posts WHERE author_id = ? AND status = ?',
params: [userId, 'published'],
})
Using SQLite
You can also integrate custom ORMs or wrapper libraries to enhance your database experience. For example, you can use Kysely or Drizzle to create a type-safe database layer on top of the Calljmp SQLite database.
File Storage
Calljmp provides secure cloud storage with bucket organization and access controls.
Upload Files
Upload various types of content:
// Upload text content
const textFile = await calljmp.storage.upload({
bucketId: 'documents',
key: 'messages/hello.txt',
content: 'Hello, world! This is my first text file.',
type: 'text/plain',
description: 'A simple greeting',
tags: ['text', 'greeting'],
})
if (textFile.error) {
console.error('Upload failed:', textFile.error)
return
}
console.log('File uploaded:', textFile.data.key)
console.log('File size:', textFile.data.size, 'bytes')
Upload Binary Files
// Upload image from blob
const imageBlob = new Blob([imageData], { type: 'image/jpeg' })
const imageFile = await calljmp.storage.upload({
bucketId: 'user-uploads',
key: `profiles/user_${userId}/avatar.jpg`,
content: imageBlob,
type: 'image/jpeg',
description: 'User profile picture',
tags: ['profile', 'avatar', `user:${userId}`],
})
List Files
Browse files in a bucket:
const result = await calljmp.storage.list({
bucketId: 'user-uploads',
limit: 50,
offset: 0,
})
if (result.error) {
console.error('Failed to list files:', result.error)
return
}
console.log(`Found ${result.data.files.length} files`)
result.data.files.forEach(file => {
console.log(`${file.key}: ${file.size} bytes`)
console.log(` Description: ${file.description}`)
console.log(` Tags: ${file.tags}`)
})
Download Files
Retrieve file content:
// Get file metadata without downloading content
const fileInfo = await calljmp.storage.peek({
bucketId: 'user-uploads',
key: 'profiles/user_123/avatar.jpg',
})
if (fileInfo.error) {
console.error('Peek failed:', fileInfo.error)
return
}
console.log('File size:', fileInfo.data.size, 'bytes')
console.log('Content type:', fileInfo.data.type)
// Download file content
const fileContent = await calljmp.storage.retrieve({
bucketId: 'user-uploads',
key: 'profiles/user_123/avatar.jpg',
})
Update File Metadata
await calljmp.storage.update({
bucketId: 'user-uploads',
key: 'profiles/user_123/avatar.jpg',
description: 'Updated profile picture',
tags: ['profile', 'avatar', 'updated'],
})
Delete Files
await calljmp.storage.delete({
bucketId: 'user-uploads',
key: 'profiles/user_123/old_avatar.jpg',
})
Custom Services
Calljmp provides a simple way to access your backend services. You can make HTTP requests to your service endpoints using the calljmp.service
.
Basic Service Calls
// Simple GET request
const result = await calljmp.service
.request('/')
.get()
.json<{ message: string }>()
if (result.error) {
console.error('Service call failed:', result.error)
return
}
console.log('Response:', result.data)
POST Requests with Data
// POST request with JSON body
const response = await calljmp.service
.request('/api/posts')
.post()
.json({
title: 'My First Post',
content: 'This is the content of my first post.',
tags: ['tutorial', 'beginner'],
})
.json<{ id: number; title: string }>()
if (response.error) {
console.error('Failed to create post:', response.error)
return
}
console.log('Created post:', response.data)
Custom Headers and Query Parameters
// Request with custom headers and query parameters
const response = await calljmp.service
.request('/api/search')
.headers({
'X-Custom-Header': 'value',
'Accept-Language': 'en-US',
})
.params({
q: 'react native development',
limit: '20',
offset: '0',
})
.get()
.json<SearchResults>()
Error Handling
Calljmp uses a consistent error handling pattern across all operations:
// All operations return Result<T, CalljmpError>
const result = await calljmp.database.query({
sql: 'SELECT * FROM users',
params: [],
})
if (result.error) {
const error = result.error
switch (error.code) {
case 'unauthorized':
console.log('User not authenticated')
// Redirect to login screen
break
case 'forbidden':
console.log('User lacks permission for this operation')
break
case 'usage_exceeded':
console.log('Usage limit exceeded')
// Show upgrade prompt
break
case 'network_error':
console.log('Network connection failed')
// Show retry option
break
default:
console.log('Unexpected error:', error.message)
}
} else {
// Success - use result.data
const data = result.data
// Process successful result
}
Common Error Codes
unauthorized
: User is not authenticatedforbidden
: User lacks required permissionsbad_request
: Invalid request parametersnot_found
: Requested resource doesn't existusage_exceeded
: Account usage limits exceedednetwork_error
: Network connectivity issuesinternal
: Server-side error occurred
Best Practices
Security
- Use User Tags: Implement role-based access control with user tags
- Parameterized Queries: Always use parameters to prevent SQL injection
- Validate Input: Validate all user input before database operations
- Least Privilege: Give users minimum required permissions
Performance
- Limit Query Results: Use LIMIT and OFFSET for pagination
- Index Database Columns: Create indexes for frequently queried columns
- Cache Results: Cache frequently accessed data locally
- Optimize File Uploads: Compress images and limit file sizes
Development
- Handle Errors: Always check for errors in result objects
- Test Offline: Ensure graceful handling of network failures
- Monitor Usage: Track API usage to avoid limits
Security
Calljmp provides robust security features to ensure that your app and its data are protected.
App Attestation (iOS)
Calljmp utilizes Apple's App Attestation for verifying the integrity of your iOS app. This ensures that only legitimate apps are communicating with your backend.
For more information, refer to Apple's App Attestation documentation.
Play Integrity (Android)
For Android apps, Google Play Integrity helps ensure that your app is genuine and hasn't been tampered with.
For more details, check out Google Play Integrity documentation.
These features enhance the security of your app, ensuring that your backend communications are from trusted sources.
Next Steps
For more advanced topics and examples, check out:
- Cloud Service Documentation
- CLI Documentation
- Flutter SDK (for cross-platform reference)