UNPKG

@dataql/firebase-adapter

Version:

Firebase adapter for DataQL with zero API changes

514 lines (406 loc) 12.9 kB
# @dataql/firebase-adapter Migrate from Firebase/Firestore to DataQL with zero API changes. This adapter provides a Firebase-compatible API that runs on DataQL with automatic scaling, caching, and offline support. ## Installation ```bash npm install @dataql/core @dataql/firebase-adapter ``` ## Quick Start ```typescript import { initializeApp, getFirestore } from "@dataql/firebase-adapter"; // Initialize Firebase app with DataQL const firebaseConfig = { apiKey: "your-api-key", authDomain: "your-project.firebaseapp.com", projectId: "your-project", storageBucket: "your-project.appspot.com", messagingSenderId: "123456789", appId: "1:123456789:web:abcdef123456", }; const app = initializeApp(firebaseConfig, { appToken: "your-app-token", // Required for DataQL authentication env: "prod", // Environment: 'dev' or 'prod' dbName: "your_app_db", // Database name for data isolation }); const db = getFirestore(app); // Use familiar Firestore syntax - all operations powered by DataQL const usersRef = db.collection("users"); // Add a document const docRef = await usersRef.add({ name: "John Doe", email: "john@example.com", age: 30, active: true, }); console.log("Document written with ID: ", docRef.id); // Query documents const snapshot = await usersRef .where("active", "==", true) .where("age", ">=", 18) .orderBy("name") .limit(10) .get(); snapshot.forEach((doc) => { console.log(doc.id, " => ", doc.data()); }); // Real-time listener const unsubscribe = usersRef.onSnapshot((snapshot) => { snapshot.forEach((doc) => { console.log("User:", doc.data()); }); }); // Clean up listener unsubscribe(); ``` ## Configuration ```typescript const app = initializeApp(firebaseConfig, { appToken: "your-app-token", // Required - authentication for DataQL env: "prod", // Optional - 'dev' or 'prod' (default: 'prod') devPrefix: "dev_", // Optional - prefix for dev environment collections dbName: "your_app_db", // Optional - database name for data isolation customConnection: undefined, // Optional - for custom integrations }); ``` ### Configuration Options - **appToken** (required): Authentication token for DataQL - **env**: Environment - 'dev' or 'prod' (default: 'prod') - **devPrefix**: Collection prefix for development environment (default: 'dev\_') - **dbName**: Database name for data isolation (each client gets dedicated database) - **customConnection**: Advanced option for custom integrations ## Benefits Over Direct Firebase While maintaining 100% Firebase API compatibility, you get DataQL's enhanced capabilities: - **Simplified Setup**: No need to manage Firebase projects, billing, or infrastructure - **Auto-scaling**: Automatic scaling based on usage - **Offline-first**: Built-in offline support with automatic sync when online - **Real-time**: Live data updates across all connected clients - **Global Performance**: Data served from edge locations worldwide for low latency - **Data Isolation**: Each client gets their own dedicated database automatically - **Multi-layer Caching**: Optimized performance with intelligent caching ## Migration Guide ### From Firebase 1. **Replace imports**: ```typescript // Before import { initializeApp } from "firebase/app"; import { getFirestore } from "firebase/firestore"; // After import { initializeApp, getFirestore } from "@dataql/firebase-adapter"; ``` 2. **Update app initialization**: ```typescript // Before - Direct Firebase connection const app = initializeApp(firebaseConfig); // After - DataQL authentication const app = initializeApp(firebaseConfig, { appToken: "your-app-token", // Required for DataQL authentication dbName: "your_app_db", // Your database name }); ``` 3. **Your Firestore code works exactly the same**: ```typescript // This works exactly the same - but now powered by DataQL const db = getFirestore(app); const snapshot = await db.collection("users").get(); ``` ## API Compatibility ### Supported Firebase Features #### Firestore Database - ✅ `initializeApp(config, options)` - Initialize Firebase app - ✅ `getFirestore(app)` - Get Firestore instance - ✅ `collection(path)` - Get collection reference - ✅ `doc(path)` - Get document reference #### Collection Operations - ✅ `.add(data)` - Add document to collection - ✅ `.doc(id)` - Get document by ID - ✅ `.where(field, operator, value)` - Filter documents - ✅ `.orderBy(field, direction)` - Sort documents - ✅ `.limit(count)` - Limit number of results - ✅ `.get()` - Execute query and get snapshot - ✅ `.onSnapshot(callback)` - Real-time listener #### Document Operations - ✅ `.get()` - Get document snapshot - ✅ `.set(data, options)` - Set document data - ✅ `.update(data)` - Update document fields - ✅ `.delete()` - Delete document - ✅ `.onSnapshot(callback)` - Real-time document listener #### Query Operators - ✅ `==` - Equal to - ✅ `!=` - Not equal to - ✅ `<` - Less than - ✅ `<=` - Less than or equal to - ✅ `>` - Greater than - ✅ `>=` - Greater than or equal to - ✅ `array-contains` - Array contains value - ✅ `in` - Field value is in array - ✅ `array-contains-any` - Array contains any of the values #### Response Types - ✅ `DocumentSnapshot` with `.data()`, `.get()`, `.exists` - ✅ `QuerySnapshot` with `.docs`, `.size`, `.empty`, `.forEach()` - ✅ `QueryDocumentSnapshot` with `.data()`, `.get()` - ✅ `WriteResult` with `.writeTime` ### DataQL Enhancements While maintaining Firebase compatibility, you also get DataQL's additional features: - **Offline-first**: Automatic offline support and sync - **Multi-region**: Global data distribution - **Schema validation**: Optional schema enforcement - **WAL support**: Write-ahead logging for reliability - **Unique document creation**: `createUnique()` method to prevent duplicates ## TypeScript Support Full TypeScript support with inferred types: ```typescript import { initializeApp, getFirestore, DocumentData, } from "@dataql/firebase-adapter"; interface User { name: string; email: string; age?: number; active: boolean; createdAt?: string; } const app = initializeApp(firebaseConfig, options); const db = getFirestore(app); // Type-safe operations const usersRef = db.collection("users"); const userDoc = await usersRef.doc("user123").get(); if (userDoc.exists) { const userData = userDoc.data() as User; console.log("User:", userData.name); } // Typed queries const activeUsers = await usersRef.where("active", "==", true).get(); activeUsers.forEach((doc) => { const user = doc.data() as User; console.log("Active user:", user.name); }); ``` ## Real-time Subscriptions Real-time features work just like Firebase: ```typescript // Collection listener const unsubscribe = db.collection("messages").onSnapshot((snapshot) => { snapshot.docChanges().forEach((change) => { if (change.type === "added") { console.log("New message:", change.doc.data()); } else if (change.type === "modified") { console.log("Modified message:", change.doc.data()); } else if (change.type === "removed") { console.log("Removed message:", change.doc.data()); } }); }); // Document listener const docUnsubscribe = db.doc("users/user123").onSnapshot((doc) => { if (doc.exists) { console.log("Current data:", doc.data()); } else { console.log("Document does not exist"); } }); // Cleanup unsubscribe(); docUnsubscribe(); ``` ## Advanced Queries Complex queries with multiple filters and ordering: ```typescript // Complex filtering const posts = await db .collection("posts") .where("published", "==", true) .where("category", "in", ["tech", "science"]) .where("likes", ">=", 10) .orderBy("createdAt", "desc") .limit(20) .get(); posts.forEach((doc) => { console.log("Post:", doc.data()); }); // Array queries const usersByTags = await db .collection("users") .where("tags", "array-contains", "developer") .get(); const usersByMultipleTags = await db .collection("users") .where("tags", "array-contains-any", ["developer", "designer"]) .get(); // Compound queries const recentActiveUsers = await db .collection("users") .where("active", "==", true) .where("lastLoginAt", ">=", "2024-01-01") .orderBy("lastLoginAt", "desc") .limit(50) .get(); ``` ## Document Operations Standard Firestore document operations: ```typescript const userRef = db.collection("users").doc("user123"); // Create/set document await userRef.set({ name: "John Doe", email: "john@example.com", active: true, }); // Update specific fields await userRef.update({ name: "John Smith", lastUpdated: new Date(), }); // Merge data with existing document await userRef.set( { preferences: { theme: "dark", notifications: true, }, }, { merge: true } ); // Get document const doc = await userRef.get(); if (doc.exists) { console.log("User data:", doc.data()); } else { console.log("User not found"); } // Delete document await userRef.delete(); ``` ## Error Handling Standard Firebase error handling: ```typescript try { const doc = await db.collection("users").doc("user123").get(); if (doc.exists) { console.log("User:", doc.data()); } else { console.log("User not found"); } } catch (error) { console.error("Error getting user:", error); } // Handling write operations try { await db.collection("users").add({ name: "Jane Doe", email: "jane@example.com", }); console.log("User created successfully"); } catch (error) { console.error("Error creating user:", error); } ``` ## Limitations Some advanced Firebase features are not yet supported: - **Authentication**: Firebase Auth methods are not implemented (implement your own authentication layer) - **Storage**: Firebase Storage methods are not implemented (use DataQL's storage instead) - **Cloud Functions**: Firebase Functions are not supported (use DataQL's serverless functions instead) - **Security Rules**: Not implemented (use DataQL's security features instead) - **Subcollections**: Nested collections are not fully implemented - **Transactions**: Atomic transactions are not yet supported - **Batch writes**: Batch operations are not yet supported - **Pagination cursors**: `startAt`, `endAt` methods are simplified If you need these features, please [open an issue](https://github.com/dataql/dataql/issues). ## Examples ### Basic CRUD Operations ```typescript const db = getFirestore(app); // Create const newUserRef = await db.collection("users").add({ name: "Alice Johnson", email: "alice@example.com", role: "admin", createdAt: new Date(), }); // Read const userSnapshot = await db .collection("users") .where("role", "==", "admin") .get(); userSnapshot.forEach((doc) => { console.log("Admin user:", doc.data()); }); // Update await db.collection("users").doc(newUserRef.id).update({ name: "Alice Smith", updatedAt: new Date(), }); // Delete await db.collection("users").doc(newUserRef.id).delete(); ``` ### Real-time Chat Application ```typescript // Listen for new messages const messagesRef = db.collection("messages"); const unsubscribe = messagesRef .orderBy("timestamp", "desc") .limit(50) .onSnapshot((snapshot) => { snapshot.forEach((doc) => { const message = doc.data(); addMessageToUI(message); }); }); // Send a message async function sendMessage(text: string, userId: string) { try { await messagesRef.add({ text, userId, timestamp: new Date(), reactions: [], }); } catch (error) { console.error("Failed to send message:", error); } } // Cleanup function cleanup() { unsubscribe(); } ``` ### E-commerce Product Catalog ```typescript // Query products by category with pagination async function getProducts(category: string, limit: number = 20) { const productsRef = db.collection("products"); const snapshot = await productsRef .where("category", "==", category) .where("inStock", "==", true) .orderBy("price", "asc") .limit(limit) .get(); return snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); } // Search products by tags async function searchProducts(tags: string[]) { const productsRef = db.collection("products"); const snapshot = await productsRef .where("tags", "array-contains-any", tags) .where("available", "==", true) .get(); return snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); } // Update product inventory async function updateInventory(productId: string, quantity: number) { const productRef = db.collection("products").doc(productId); await productRef.update({ inventory: quantity, inStock: quantity > 0, lastUpdated: new Date(), }); } ``` ## License MIT