UNPKG

@e-techsolutions/e-baas-sdk

Version:

Official E-BaaS TypeScript SDK - Backend as a Service client library

663 lines (521 loc) 18.4 kB
# E-BaaS SDK The official TypeScript SDK for E-BaaS (Enterprise Backend as a Service) - A comprehensive backend-as-a-service platform. ## Installation ```bash npm install @e-techsolutions/e-baas-sdk@latest # or yarn add @e-techsolutions/e-baas-sdk@latest ``` ## Quick Start ```typescript import { createClient } from "@e-techsolutions/e-baas-sdk"; // Basic configuration (required) const EBAAS_URL = process.env.EBAAS_API_URL; // required - https://your-api-e-baas-service const EBAAS_KEY = process.env.EBAAS_API_KEY; // required - your API key const WORKSPACE_ID = process.env.EBAAS_WORKSPACE_ID; // required - your workspace ID export const ebaas = createClient(EBAAS_URL, EBAAS_KEY, { // Database configuration (required) database: { workspaceId: WORKSPACE_ID, // required autoDetectCustomDatabase: true, // optional (default: false) }, // Other configurations are optional }); ``` ### Complete Authentication Flow ```typescript // 1. User login const { user, session, error } = await ebaas.auth.signIn({ email: "user@example.com", password: "secure-password", }); if (error) { console.error("Login failed:", error); return; } // 2. Configure session in SDK (REQUIRED!) ebaas.auth.setSession(session); // 3. Persist session (optional) localStorage.setItem("ebaas_session", JSON.stringify(session)); localStorage.setItem("ebaas_user", JSON.stringify(user)); // 4. Now you can use database operations normally const query = ebaas.from("users").select("*"); const { data, error } = await query.execute(); if (error) { console.error("Error fetching users:", error); } else { console.log("Users found:", data.length); } ``` ### Session Restoration ```typescript // Restore session on app initialization const restoreSession = () => { const savedSession = localStorage.getItem("ebaas_session"); const savedUser = localStorage.getItem("ebaas_user"); if (savedSession && savedUser) { const sessionData = JSON.parse(savedSession); // Check if not expired if (sessionData.expires_at > Date.now()) { // Restore session in SDK ebaas.auth.setSession(sessionData); console.log("✅ Session restored automatically"); } else { // Clear expired session localStorage.removeItem("ebaas_session"); localStorage.removeItem("ebaas_user"); } } }; ``` ### Environment Variables Create a `.env` file in your project root: ```env EBAAS_API_URL=https://your-api-e-baas-service EBAAS_API_KEY=your-api-key-here EBAAS_WORKSPACE_ID=your-workspace-id-here ``` **Note**: This works with any bundler (Webpack, Vite, Next.js, etc.) that supports `process.env`. ## Features - **Authentication** - Email/password and OAuth providers (Google, GitHub, Facebook) - **Database** - PostgreSQL with real-time subscriptions and RPC support - **Custom Database** - Use your own PostgreSQL database with automatic detection - **Storage** - File uploads with CDN integration and image transformations - **Realtime** - WebSocket subscriptions for live data updates - **Edge Functions** - Serverless functions with dual runtime (Node.js VM + Deno) and native SDK integration - **TypeScript** - Full type safety and IntelliSense support ## Usage Examples ### Authentication ```typescript // Sign up with email/password const { user, session, error } = await ebaas.auth.signUp({ email: "user@example.com", password: "secure-password", name: "John Doe", }); // Sign in const { user, session, error } = await ebaas.auth.signIn({ email: "user@example.com", password: "secure-password", }); // Get current user const user = await ebaas.auth.getUser(); // Sign out await ebaas.auth.signOut(); ``` **⚠️ IMPORTANT - Session Configuration:** After login, you **MUST** configure the session in the SDK for all subsequent operations to work correctly: ```typescript // Complete authentication flow const loginUser = async () => { // 1. Perform login const { user, session, error } = await ebaas.auth.signIn({ email: "user@example.com", password: "secure-password", }); if (error) { console.error("Login failed:", error); return; } // 2. Configure session in SDK (MANDATORY STEP!) ebaas.auth.setSession(session); // 3. Persist session in localStorage (optional) localStorage.setItem("ebaas_session", JSON.stringify(session)); localStorage.setItem("ebaas_user", JSON.stringify(user)); console.log("✅ Login and session configuration completed"); }; // Restore session on application initialization const restoreSession = () => { const savedSession = localStorage.getItem("ebaas_session"); const savedUser = localStorage.getItem("ebaas_user"); if (savedSession && savedUser) { const sessionData = JSON.parse(savedSession); // Check if not expired if (sessionData.expires_at > Date.now()) { // Restore session in SDK ebaas.auth.setSession(sessionData); console.log("✅ Session restored automatically"); } else { // Clear expired session localStorage.removeItem("ebaas_session"); localStorage.removeItem("ebaas_user"); } } }; ``` ### Database Operations ```typescript // Select data const query = ebaas.from("users").select("*"); const { data, error } = await query.execute(); if (error) { console.error("Error fetching users:", error); } else { console.log("Users found:", data.length); } // Select specific fields for better performance const query = ebaas .from("users") .select("id, name, email") .eq("status", "active") .order("created_at", { ascending: false }) .limit(10); const { data, error } = await query.execute(); // Insert data const query = ebaas.from("users").insert({ name: "John Doe", email: "john@example.com", }); const { data, error } = await query.execute(); if (error) { console.error("Error creating user:", error); } else { console.log("User created successfully:", data); } // Update data const query = ebaas.from("users").update({ name: "John Smith" }).eq("id", 123); const { data, error } = await query.execute(); if (error) { console.error("Error updating user:", error); } else { console.log("User updated successfully:", data); } // Delete data const query = ebaas.from("users").delete().eq("id", 123); const { data, error } = await query.execute(); if (error) { console.error("Error deleting user:", error); } else { console.log("User deleted successfully"); } // RPC (stored procedures) const query = ebaas.rpc("get_user_stats", { user_id: 123, }); const { data, error } = await query.execute(); ``` ### Custom Database ```typescript // Configure with automatic custom database detection const ebaas = createClient(EBAAS_URL, EBAAS_KEY, { database: { workspaceId: WORKSPACE_ID, autoDetectCustomDatabase: true, // Enable custom database auto-detection }, }); ``` ### Storage ```typescript // Upload file const { data, error } = await ebaas.storage.from("avatars").upload("user-123.jpg", file, { cacheControl: "3600", upsert: true, }); // Download file const { data, error } = await ebaas.storage.from("avatars").download("user-123.jpg"); // Get public URL const { data } = ebaas.storage.from("avatars").getPublicUrl("user-123.jpg"); // Get signed URL (for private files) const { data, error } = await ebaas.storage.from("private-docs").createSignedUrl("document.pdf", 60); // 60 seconds // List files const { data, error } = await ebaas.storage.from("avatars").list("", { limit: 100, offset: 0, sortBy: { column: "name", order: "asc" }, }); ``` ### Realtime Subscriptions ```typescript // Subscribe to database changes const channel = ebaas.channel("realtime:users"); channel.onPostgresChanges( { event: "*", // INSERT, UPDATE, DELETE, or * schema: "public", table: "users", }, (payload) => { console.log("Change received!", payload); } ); channel.subscribe(); // Broadcast messages channel.send({ type: "broadcast", event: "message", payload: { text: "Hello World!" }, }); // Presence tracking channel.track({ user_id: 123, status: "online" }); // Unsubscribe channel.unsubscribe(); ``` ### Edge Functions #### Client-side Function Invocation ```typescript // Invoke authenticated function (requires session) const { data, error } = await ebaas.functions.invoke("hello-world", { body: { name: "John" }, headers: { "Content-Type": "application/json" }, }); // Invoke public function (no authentication required) const response = await fetch("https://your-api-domain.com/functions/v1/public/hello-world", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "John" }), }); const data = await response.json(); // List functions const { data, error } = await ebaas.functions.list(); // Get function details const { data, error } = await ebaas.functions.get("hello-world"); ``` #### Native SDK Integration in Edge Functions The SDK is natively available within Edge Functions with global methods: ```typescript // Edge Function with native SDK access const users = await from("users").select("*").limit(10); const bucket = bucket("uploads"); await channel("notifications").send("user_created", { user: userData }); return createResponse({ success: true, users }); ``` **Available globals**: `from()`, `rpc()`, `bucket()`, `channel()`, `invoke()`, `ebaas.*` ## Advanced Configuration ```typescript const ebaas = createClient( "https://your-api-domain.com", // required - API URL "your-api-key", // required - API Key { // Database configuration (required) database: { workspaceId: "your-workspace-id", // required - Workspace ID autoDetectCustomDatabase: true, // optional (default: false) }, // Authentication configuration (optional) auth: { autoRefreshToken: true, // optional (default: true) persistSession: true, // optional (default: true) detectSessionInUrl: true, // optional (default: true) }, // Realtime configuration (optional) realtime: { params: { eventsPerSecond: 10, // optional (default: 10) }, }, // Global configuration (optional) global: { headers: { "x-custom-header": "custom-value", // optional - custom headers }, }, } ); ``` ## Error Handling ```typescript import { EBaaSError } from "@e-techsolutions/e-baas-sdk"; try { const query = ebaas.from("users").select("*"); const { data, error } = await query.execute(); } catch (error) { if (error instanceof EBaaSError) { console.error("E-BaaS Error:", error.message); console.error("Status:", error.status); console.error("Details:", error.details); } } ``` ## Secure DELETE Operations ### Security Validation E-BaaS implements strict validation for DELETE and UPDATE operations, requiring **at least one filter** to prevent accidental mass deletions: ```typescript // ✅ CORRECT: DELETE with specific filter const query = ebaas.from("users").delete().eq("id", 123); const { data, error } = await query.execute(); // ❌ ERROR: DELETE without filters (returns "Security violation") const query = ebaas.from("users").delete(); const { data, error } = await query.execute(); // Fails with security error ``` ### Secure DELETE Operations with Required Filters Always use the SDK with proper filters for safe DELETE operations: ```typescript // DELETE using SDK with required filter const query = ebaas.from("users").delete().eq("id", userId); const { data, error } = await query.execute(); if (error) { console.error("Error deleting user:", error); } else { console.log("User deleted successfully"); } ``` ## Refresh Token Management ### Automatic Token Refresh (Default) The E-BaaS SDK automatically manages refresh tokens for workspace users, ensuring seamless authentication without manual intervention: ```typescript const ebaas = createClient(EBAAS_URL, EBAAS_KEY, { database: { workspaceId: WORKSPACE_ID, }, auth: { autoRefreshToken: true, // Default: true - automatic refresh enabled persistSession: true, // Default: true - session persistence }, }); ``` ### How Automatic Refresh Works 1. **Login Phase**: When a user logs in, they receive both an access token and a refresh token 2. **Background Monitoring**: The SDK monitors token expiration automatically 3. **Seamless Refresh**: Before expiration, the SDK calls `/auth/v1/sdk/refresh` to get new tokens 4. **Token Rotation**: Old refresh tokens are invalidated for security (refresh token rotation) 5. **Continued Operation**: All database operations continue without interruption ### Manual Refresh Token Management For advanced use cases, you can disable automatic refresh and manage tokens manually: ```typescript const ebaas = createClient(EBAAS_URL, EBAAS_KEY, { database: { workspaceId: WORKSPACE_ID, }, auth: { autoRefreshToken: false, // Disable automatic refresh }, }); // Manual refresh when needed const refreshTokens = async () => { try { const query = ebaas.auth.refreshSession(); const { data, error } = await query.execute(); if (error) { console.error('Failed to refresh token:', error); // Redirect to login page return; } // Update session in SDK ebaas.auth.setSession(data.session); // Update localStorage localStorage.setItem('ebaas_session', JSON.stringify(data.session)); console.log('✅ Tokens refreshed successfully'); } catch (error) { console.error('Token refresh error:', error); // Handle refresh failure - redirect to login } }; ``` ### React Hook for Token Management Here's a custom React hook for handling token refresh and expiration: ```typescript import { useEffect, useCallback } from 'react'; import { ebaas } from './ebaas-client'; export const useTokenRefresh = () => { const checkTokenExpiration = useCallback(async () => { const session = ebaas.auth.getSession(); if (!session?.expires_at) return; const expiresAt = new Date(session.expires_at); const now = new Date(); const timeToExpiry = expiresAt.getTime() - now.getTime(); // Refresh if token expires in less than 5 minutes if (timeToExpiry < 5 * 60 * 1000) { try { const query = ebaas.auth.refreshSession(); const { data, error } = await query.execute(); if (error) { console.error('Token refresh failed:', error); // Redirect to login window.location.href = '/login'; return; } // Update session ebaas.auth.setSession(data.session); localStorage.setItem('ebaas_session', JSON.stringify(data.session)); console.log('✅ Token refreshed proactively'); } catch (error) { console.error('Token refresh error:', error); // Handle error - redirect to login window.location.href = '/login'; } } }, []); useEffect(() => { // Check immediately checkTokenExpiration(); // Check every 2 minutes const interval = setInterval(checkTokenExpiration, 2 * 60 * 1000); return () => clearInterval(interval); }, [checkTokenExpiration]); }; // Usage in your main App component const App = () => { useTokenRefresh(); // Add this hook to your main App return ( <div> {/* Your app content */} </div> ); }; ``` ### Token Refresh Error Handling ```typescript const handleDatabaseOperation = async () => { try { const query = ebaas.from('users').select('*'); const { data, error } = await query.execute(); if (error) { // Check if error is due to token expiration if (error.status === 401 || error.message?.includes('token')) { console.log('Token expired, attempting refresh...'); const { session, error } = await ebaas.auth.refreshSession(); if (error) { // Refresh failed - redirect to login console.error('Token refresh failed:', error); window.location.href = '/login'; return; } // Update session and retry operation ebaas.auth.setSession(session); const { data, error } = await query.execute(); if (!error) { console.log('✅ Operation successful after token refresh'); return data; } } console.error('Database operation failed:', error); } return data; } catch (error) { console.error('Unexpected error:', error); } }; ``` ### Security Benefits - **Token Rotation**: Each refresh generates new tokens and invalidates old ones - **Short-lived Access Tokens**: Access tokens have shorter lifespans for security - **Automatic Cleanup**: Expired and revoked tokens are automatically cleaned up - **Session Tracking**: Each session is tracked with IP address and user agent - **Workspace Isolation**: Refresh tokens are isolated per workspace for multi-tenancy ### Configuration Options ```typescript const ebaas = createClient(EBAAS_URL, EBAAS_KEY, { auth: { autoRefreshToken: true, // Enable/disable automatic refresh persistSession: true, // Persist session in localStorage detectSessionInUrl: true, // Detect session from URL params }, }); ``` ## TypeScript Support The SDK is built with TypeScript and provides full type safety: ```typescript interface User { id: number; name: string; email: string; created_at: string; } const query = ebaas.from<User>("users").select("*"); const { data, error } = await query.execute(); // data is now typed as User[] | null ``` ## Edge Functions Runtime Support Enhanced Edge Functions with dual runtime support (Node.js VM + Deno) and native SDK integration. ## License MIT