UNPKG

@dataql/react-native

Version:

DataQL React Native SDK with offline-first capabilities and clean API

576 lines (453 loc) 17.2 kB
# Database Configuration Guide This guide explains how to use the new extended database configuration in DataQL React Native client. ## Overview The DataQL React Native client now supports comprehensive database configuration that gets passed through the DataQL Worker to the Lambda functions for actual database connections. The architecture flow is: **ClientWorkerLambdaDatabase** The client configuration specifies which database the worker/lambda should connect to, supporting: - **MongoDB** - Document database - **SQLite** - Local file-based database - **PostgreSQL** - Relational database - **Cloudflare D1** - Serverless SQLite ## DataQL Database Configuration Guide This guide explains how to configure database connections in DataQL React Native applications. ### Architecture Overview DataQL uses a distributed architecture: **ClientWorkerLambdaDatabase** - **MongoDB**: Handled by Lambda with automatic optimizations - **Other databases** (PostgreSQL, SQLite, D1): Handled by Workers - **Database isolation**: Each client gets their own database (no sharing) ### Configuration Modes **Database name is always required** - each client gets their own database for data isolation. 1. **Minimal Configuration**: Database name only (uses main database URL) 2. **Full Configuration**: Custom database URL + database name ```typescript // Option 1: Minimal config - uses main database URL with your database name const client = new DataQLClient({ // Worker URL is now hardcoded to https://dataql-worker.finhub.workers.dev databaseConfig: { name: "my-app-db", // Required - your dedicated database // Type auto-detected as MongoDB from main database URL }, }); // Option 2: Full configuration - custom database URL + your database name const client = new DataQLClient({ // Worker URL is now hardcoded to https://dataql-worker.finhub.workers.dev databaseConfig: { name: "my-app-db", // Required - your dedicated database url: "mongodb://user:pass@custom-server.com:27017", // Type: MongoDB (auto-detected) }, }); // Option 3: Legacy mode (no databaseConfig) - uses session database name const client = new DataQLClient({ // Worker URL is now hardcoded to https://dataql-worker.finhub.workers.dev // Uses database name from session initialization }); ``` ## DatabaseConfig Interface DataQL provides a **simplified, user-facing** database configuration interface that makes it easy to connect to different databases. The system automatically handles all internal optimizations like connection pooling, timeouts, SSL settings, and retry logic. ### User-Facing Configuration ```typescript interface DatabaseConfig { /** Database name (required - each client gets their own database) */ name: string; /** Database connection URL (optional - uses main database URL if not provided. Database type auto-detected from URL protocol) */ url?: string; } ``` **Note**: - **Database credentials should be included in the URL** (e.g., `mongodb://user:pass@host/db` or `postgresql://user:pass@host:port/db`). - The `url` field is optional. If not provided, the system will use the main database URL with your specified database name. - **Database name is always required** - each client gets their own isolated database. - **Database type is auto-detected** from URL protocol (mongodb://, postgresql://, sqlite://, file:, etc.) ### What the System Handles Automatically When you provide a `DatabaseConfig`, the DataQL system automatically applies production-ready defaults for: - **Connection Pooling**: Optimized pool sizes (min: 2, max: 10) - **Timeouts**: 30-second connection and socket timeouts - **SSL/TLS**: Secure connections enabled by default for MongoDB and PostgreSQL - **Retry Logic**: Automatic retries (3 attempts with 1-second delays) - **Pool Management**: Connection acquisition and idle timeouts You don't need to worry about these details - the system is configured with best practices out of the box. ## Architecture Flow **Important**: The client does not connect directly to databases. The flow is: ``` ClientWorkerLambdaDatabase ``` 1. **Client**: Stores operations locally for immediate response 2. **Client**: Sends database config with requests to Worker/Lambda 3. **Worker**: Extracts database config and forwards to Lambda 4. **Lambda**: Uses database config for actual database connections 5. **Lambda**: Returns results back through the chain This architecture ensures: - ✅ Offline-first operation with local storage - ✅ Transparent sync when connectivity is available - ✅ No direct database connections from client devices - ✅ Centralized database access control and security ## Configuration Examples ### MongoDB Configuration ```typescript // URL with credentials (recommended) const mongoConfig: DatabaseConfig = { name: "my-app-db", url: "mongodb://dbuser:dbpass@localhost:27017", // Type: MongoDB (auto-detected) }; // Or minimal configuration (uses main database URL) const mongoConfigSimple: DatabaseConfig = { name: "my-app-db", // No URL - uses main database URL (MONGO_URI) with your database name // Type: MongoDB (auto-detected from main database) }; const client = new DataQLClient({ // Worker URL is now hardcoded to https://dataql-worker.finhub.workers.dev databaseConfig: mongoConfig, }); ``` ### PostgreSQL Configuration ```typescript // URL with credentials (recommended) const postgresConfig: DatabaseConfig = { name: "my-app-db", url: "postgresql://dbuser:dbpass@localhost:5432/my-app-db", // Type: PostgreSQL (auto-detected) }; // Or minimal configuration (uses main database URL) const postgresConfigSimple: DatabaseConfig = { name: "my-app-db", // No URL - uses main database URL with your database name // Note: Main database is typically MongoDB, so this would fallback to MongoDB }; const client = new DataQLClient({ // Worker URL is now hardcoded to https://dataql-worker.finhub.workers.dev databaseConfig: postgresConfig, }); ``` ### SQLite Configuration (for testing) ```typescript const sqliteConfig: DatabaseConfig = { type: "sqlite", name: "app.db", url: "file:./app.db", // No username/password needed for SQLite }; const client = new DataQLClient({ // Worker URL is now hardcoded to https://dataql-worker.finhub.workers.dev databaseConfig: sqliteConfig, }); ``` ### Cloudflare D1 Configuration ```typescript const d1Config: DatabaseConfig = { type: "d1", name: "my-d1-database", url: "https://api.cloudflare.com/client/v4/accounts/{account_id}/d1/database/{database_id}", options: { // D1-specific options accountId: "your-account-id", databaseId: "your-database-id", }, }; const client = new DataQLClient({ // Worker URL is now hardcoded to https://dataql-worker.finhub.workers.dev databaseConfig: d1Config, }); ``` ## Multiple Database Support You can easily switch between different databases by changing the configuration: ```typescript // Development - use local MongoDB const devConfig: DatabaseConfig = { type: "mongodb", name: "myapp-dev", url: "mongodb://localhost:27017", }; // Production - use MongoDB Atlas const prodConfig: DatabaseConfig = { type: "mongodb", name: "myapp-prod", username: process.env.DB_USER, password: process.env.DB_PASS, url: process.env.MONGODB_URL!, }; // Use the appropriate config based on environment const client = new DataQLClient({ // Worker URL is now hardcoded to https://dataql-worker.finhub.workers.dev databaseConfig: process.env.NODE_ENV === "production" ? prodConfig : devConfig, }); ``` ## Current Support Status - ✅ **MongoDB**: Full support with connection pooling and SSL - 🚧 **PostgreSQL**: Interface ready, implementation pending - 🚧 **SQLite**: Interface ready, implementation pending - 🚧 **Cloudflare D1**: Interface ready, implementation pending ## Security Best Practices 1. **Never hardcode credentials** in your source code 2. **Use environment variables** for sensitive configuration 3. **Enable SSL/TLS** for production databases (handled automatically) 4. **Use connection pooling** for optimal performance (handled automatically) 5. **Implement proper retry logic** for network resilience (handled automatically) The DataQL system handles items 3-5 automatically when you provide a `DatabaseConfig`. ## Configuration Interface ```typescript interface DatabaseConfig { /** Database type */ type: "mongodb" | "sqlite" | "d1" | "postgres"; /** Database name */ name: string; /** Database username (optional) */ username?: string; /** Database password (optional) */ password?: string; /** Database connection URL */ url: string; /** Optional connection pool settings (for lambda) */ pool?: { min?: number; max?: number; }; /** SSL/TLS configuration (for lambda connections) */ ssl?: | boolean | { rejectUnauthorized?: boolean; ca?: string; cert?: string; key?: string; }; /** Additional database-specific options (for lambda) */ options?: Record<string, any>; } ``` ## Basic Usage ```typescript import { DataQLClient, DatabaseConfig } from "@dataql/react-native"; // Define database configuration that will be used by worker/lambda const databaseConfig: DatabaseConfig = { type: "mongodb", name: "myapp_production", username: "myapp_user", password: "secure_password_123", url: "mongodb://localhost:27017/myapp_production", }; // Create client configuration const config = { schemas: yourSchemas, database: databaseConfig, // Passed to worker/lambda syncConfig: { serverUrl: "https://dataql-worker.finhub.workers.dev", // Worker endpoint }, // ... other configuration options }; // Initialize client const client = new DataQLClient(config); await client.initialize(); ``` ## Database-Specific Configurations ### MongoDB (for Lambda Connection) ```typescript const mongoConfig: DatabaseConfig = { type: "mongodb", name: "myapp_production", username: "mongodb_user", password: "mongodb_password", url: "mongodb+srv://user:pass@cluster.mongodb.net/database", }; ``` ### PostgreSQL (for Lambda Connection) ```typescript const postgresConfig: DatabaseConfig = { type: "postgres", name: "myapp_production", username: "postgres_user", password: "postgres_password", url: "postgresql://user:pass@localhost:5432/database", }; ``` ### SQLite (for Lambda Connection) ```typescript const sqliteConfig: DatabaseConfig = { type: "sqlite", name: "myapp_local.db", url: "file:./data/myapp_local.db", }; ``` ### Cloudflare D1 (for Lambda Connection) ```typescript const d1Config: DatabaseConfig = { type: "d1", name: "myapp-d1-database", username: "cloudflare_token", password: "your_cloudflare_api_token", url: "https://api.cloudflare.com/client/v4/accounts/account-id/d1/database/db-id", }; ``` ## Environment-Based Configuration ```typescript function getDatabaseConfig(env: string): DatabaseConfig { switch (env) { case "development": return { type: "sqlite", name: "myapp_dev.db", url: "file:./data/myapp_dev.db", }; case "staging": return { type: "postgres", name: "myapp_staging", username: "staging_user", password: "staging_password", url: "postgresql://staging_user:staging_password@staging-db.com:5432/myapp_staging", }; case "production": return { type: "mongodb", name: "myapp_production", username: "prod_user", password: "prod_password", url: "mongodb+srv://prod_user:prod_password@production-cluster.mongodb.net/myapp_production", }; default: return getDatabaseConfig("development"); } } // Use in client configuration const config = { schemas: yourSchemas, database: getDatabaseConfig(process.env.NODE_ENV || "development"), syncConfig: { serverUrl: "https://dataql-worker.finhub.workers.dev", }, }; ``` ## Complete Client Configuration ```typescript import { DataQLClient, DatabaseConfig, DataQLReactNativeConfig, } from "@dataql/react-native"; const databaseConfig: DatabaseConfig = { type: "mongodb", name: "myapp_production", username: "myapp_user", password: "secure_password_123", url: "mongodb://localhost:27017/myapp_production", }; const clientConfig: DataQLReactNativeConfig = { // Required: Your data schemas schemas: yourSchemas, // Optional: App authentication token appToken: "your-app-token", // Local SQLite database name (for offline storage) databaseName: "dataql_local.db", // Database configuration (passed to worker/lambda) database: databaseConfig, // Sync configuration syncConfig: { serverUrl: "https://dataql-worker.finhub.workers.dev", // Worker endpoint autoSync: true, syncInterval: 30000, // 30 seconds retryCount: 3, batchSize: 50, }, // Additional options enableChangeListener: true, debug: true, env: "dev", }; const client = new DataQLClient(clientConfig); await client.initialize(); ``` ## Utility Functions ### Create Database Configuration ```typescript import { createDatabaseConfig } from "@dataql/react-native"; const config = createDatabaseConfig( "postgres", "myapp_db", "postgresql://user:pass@localhost:5432/myapp_db", { username: "db_user", password: "db_password", ssl: true, pool: { min: 2, max: 10, }, } ); ``` ### Validate Database Configuration ```typescript import { validateDatabaseConfig } from "@dataql/react-native"; const validation = validateDatabaseConfig(databaseConfig); if (!validation.valid) { console.error("Database configuration errors:", validation.errors); // Handle validation errors } else { // Configuration is valid - will be sent to worker/lambda const client = new DataQLClient({ database: databaseConfig, ...otherConfig, }); } ``` ## Important Notes ### Architecture Flow The DataQL architecture ensures secure and scalable database access: 1. **Client Side**: Stores data locally in SQLite for offline functionality 2. **Worker Layer**: Receives client requests and database configuration 3. **Lambda Functions**: Establish secure connections to production databases 4. **Database Layer**: Actual data storage (MongoDB, PostgreSQL, etc.) ### Transparent Operations Remember that [the DataQL API should not distinguish between offline and online operations](memory:7036908233781557754) - it should be transparent to the user. The client will automatically: 1. Store operations locally for immediate response 2. Send requests with database config to worker/lambda 3. Sync data when connectivity is available 4. Handle conflicts and maintain data consistency ### Security Considerations - Database credentials are transmitted securely to worker/lambda - Client never directly connects to production databases - Worker/lambda handles all database authentication and connection pooling - Use environment variables for sensitive credentials - Implement proper authentication between client and worker ### Performance Tips - Configure appropriate connection pool settings for lambda functions - Use read replicas for read-heavy workloads in lambda - Consider caching strategies at the worker level - Monitor lambda connection usage and adjust pool settings ## Example Implementation See `examples/database-config-usage.tsx` for a complete working example that demonstrates: - Multiple database type configurations for worker/lambda - Environment-specific configuration - Client initialization and testing - Error handling and validation ## Migration from Previous Versions If you're upgrading from a previous version: 1. Add the `database` property to your client configuration 2. Update your worker/lambda to handle the new database configurations 3. Test the new database connections in development 4. Deploy worker and lambda updates before client updates The existing `databaseName` property is still supported for local SQLite database naming and works alongside the new `database` configuration that gets passed to worker/lambda. ### Database Isolation Each client gets their own dedicated database for complete data isolation: ```typescript // Client A gets their own database const clientA = new DataQLClient({ // Worker URL is now hardcoded to https://dataql-worker.finhub.workers.dev databaseConfig: { name: "client-a-database", // Dedicated database for Client A // Type auto-detected from main database URL (MongoDB) }, }); // Client B gets their own separate database const clientB = new DataQLClient({ // Worker URL is now hardcoded to https://dataql-worker.finhub.workers.dev databaseConfig: { name: "client-b-database", // Dedicated database for Client B // Type auto-detected from main database URL (MongoDB) }, }); ``` **Connection Strategy:** - **With URL**: Creates/connects to database using provided URL + database name - **Without URL**: Uses main database URL (MONGO_URI) + your database name - **No sharing**: Each database name gets its own isolated data space