UNPKG

@enfyra/sdk-nuxt

Version:
592 lines (469 loc) 18 kB
# @enfyra/sdk-nuxt Nuxt SDK for Enfyra CMS - A powerful composable-based API client with full SSR support and TypeScript integration. ## Features **SSR & Client-Side Support** - Automatic server-side rendering with `useFetch` or client-side with `$fetch` **Authentication Integration** - Built-in auth composables with automatic header forwarding **Asset Proxy** - Automatic `/assets/**` proxy to backend with no configuration needed **TypeScript Support** - Full type safety with auto-generated declarations **Batch Operations** - Efficient bulk operations with real-time progress tracking (client-side) **Error Handling** - Automatic error management with console logging **Reactive State** - Built-in loading, error, and data states **Caching Support** - Optional cache keys for SSR mode optimization ## Installation ```bash npm install @enfyra/sdk-nuxt ``` ## Setup Add the module to your `nuxt.config.ts`: ```typescript export default defineNuxtConfig({ modules: ["@enfyra/sdk-nuxt"], enfyraSDK: { apiUrl: "http://localhost:1105", // Only apiUrl is required }, }) ``` ### Automatic App URL Detection The SDK automatically detects your application URL: - **Client-side**: Uses `window.location.origin` - **Server-side**: Detects from request headers (supports proxies with `X-Forwarded-*` headers) - **No configuration needed**: Works out of the box with any deployment ## Quick Start ### SSR Mode - Perfect for Page Data ```typescript // pages/users.vue <script setup> // Automatic execution on server-side with caching (runs immediately, no execute() needed) const { data: users, pending, error, refresh } = useEnfyraApi('/users', { ssr: true, key: 'users-list' // Optional cache key }); </script> <template> <div> <div v-if="pending">Loading users...</div> <div v-else-if="error">Error: {{ error }}</div> <div v-else> <h1>Users ({{ users?.meta?.totalCount }})</h1> <UserCard v-for="user in users?.data" :key="user.id" :user="user" /> <button @click="refresh">Refresh</button> </div> </div> </template> ``` ### Client Mode - Perfect for User Interactions ```typescript // components/CreateUserForm.vue <script setup> // Manual execution control for form submissions const { execute: createUser, pending, error } = useEnfyraApi('/users', { method: 'post', errorContext: 'Create User' }); const formData = reactive({ name: '', email: '' }); async function handleSubmit() { await createUser({ body: formData }); if (!error.value) { toast.success('User created successfully!'); await navigateTo('/users'); } } </script> ``` ### Authentication ```typescript <script setup> const { me, login, logout, isLoggedIn } = useEnfyraAuth(); // Login await login({ email: 'user@example.com', password: 'password123' }); // Check auth status console.log('Logged in:', isLoggedIn.value); console.log('Current user:', me.value); // Logout await logout(); </script> ``` ### Asset URLs - Automatic Proxy The SDK automatically proxies all asset requests to your backend. Simply use `/assets/**` paths directly: ```vue <template> <!-- Assets are automatically proxied to your backend --> <img src="/assets/images/logo.svg" alt="Logo" /> <img :src="`/assets/images/users/${user.id}/avatar.jpg`" alt="Avatar" /> <!-- Works with any asset type --> <video src="/assets/videos/intro.mp4" controls /> <a :href="`/assets/documents/${doc.filename}`" download>Download PDF</a> </template> ``` **How it works:** - All requests to `/assets/**` are automatically proxied to `{apiUrl}/enfyra/api/assets/**` - No configuration needed - works out of the box - Supports all asset types: images, videos, documents, etc. - Maintains proper authentication headers ## Core Composables ### `useEnfyraApi<T>(path, options)` Main composable for API requests with both SSR and client-side support. ```typescript // SSR Mode - Runs immediately (like useFetch) const { data, pending, error, refresh } = useEnfyraApi('/endpoint', { ssr: true, key: 'cache-key', // Optional method: 'get', query: { page: 1 } }); // ⚠️ Returns useFetch result: { data, pending, error, refresh } // Client Mode - Manual execution const { data, pending, error, execute } = useEnfyraApi('/endpoint', { method: 'post', errorContext: 'Create Resource' }); // ⚠️ Returns custom result: { data, pending, error, execute } await execute({ body: { name: 'New Item' }, id: '123' // For /endpoint/123 }); ``` **Options:** - `ssr?: boolean` - Enable server-side rendering mode (executes immediately like useFetch) - `method?: 'get' | 'post' | 'patch' | 'delete'` - HTTP method - `body?: any` - Request body (POST/PATCH) - `query?: Record<string, any>` - URL query parameters - `headers?: Record<string, string>` - Custom headers - `errorContext?: string` - Error context for logging - `onError?: (error: ApiError, context?: string) => void` - Custom error handler - `key?: string` - Cache key (SSR mode, optional) - `default?: () => T` - Default value (SSR mode only) **Batch Options (only available for PATCH, DELETE, and POST methods):** - `batchSize?: number` - Batch size for chunking large operations (default: no limit) - `concurrent?: number` - Maximum concurrent requests (default: no limit) - `onProgress?: (progress: BatchProgress) => void` - Real-time progress callback for batch operations > 🎯 **TypeScript Smart:** Batch options (`batchSize`, `concurrent`, `onProgress`) are only available in TypeScript IntelliSense when using methods that support batch operations (PATCH, DELETE, POST). For GET and PUT methods, these options won't appear in autocomplete. **⚠️ Important: Return Types Differ** - **SSR Mode**: Returns `useFetch` result `{ data, pending, error, refresh }` - **Client Mode**: Returns custom result `{ data, pending, error, execute }` **Execute Options (Client mode only):** **Basic Options:** - `id?: string | number` - Single resource ID - `body?: any` - Override request body **Batch Options (only when using `ids` or `files`):** - `ids?: (string | number)[]` - Batch operation IDs (PATCH/DELETE) - `files?: FormData[]` - Array of FormData objects for batch upload (POST) - `batchSize?: number` - Override batch size for this execution - `concurrent?: number` - Override concurrent limit for this execution - `onProgress?: (progress: BatchProgress) => void` - Override progress callback for this execution > 🎯 **TypeScript Smart:** Batch execute options (`batchSize`, `concurrent`, `onProgress`) are only available when you provide `ids` or `files` parameters, ensuring type safety. ### `useEnfyraAuth()` Authentication composable with reactive state management. ```typescript const { me, login, logout, fetchUser, isLoggedIn } = useEnfyraAuth(); // Properties me.value // Current user data (reactive) isLoggedIn.value // Auth status (computed) // Methods await login({ email, password }) // Login user await logout() // Logout user await fetchUser() // Refresh user data ``` ## Advanced Usage ### Batch Operations ```typescript // Basic batch delete - unlimited parallel requests // 🎯 Note: Batch options only appear in IntelliSense for DELETE method const { execute: deleteItems } = useEnfyraApi('/items', { method: 'delete', errorContext: 'Delete Items' }); await deleteItems({ ids: ['1', '2', '3'] }); // Advanced batch operations with concurrency control // 🎯 TypeScript shows batch options (batchSize, concurrent, onProgress) for DELETE method const { execute: deleteMany } = useEnfyraApi('/users', { method: 'delete', batchSize: 10, // Available: DELETE method supports batching concurrent: 3, // Available: DELETE method supports batching onProgress: (progress) => { // Available: DELETE method supports batching console.log(`Deleting: ${progress.completed}/${progress.total}`); }, onError: (error, context) => toast.error(`${context}: ${error.message}`) }); // GET method example - batch options NOT available // 🎯 TypeScript won't show batch options for GET method const { execute: getUsers } = useEnfyraApi('/users', { method: 'get', // batchSize: 10, // Not available: GET doesn't support batching // concurrent: 3, // Not available: GET doesn't support batching // onProgress: () => {} // Not available: GET doesn't support batching errorContext: 'Fetch Users' }); // Delete 100 users in controlled batches await deleteMany({ ids: Array.from({length: 100}, (_, i) => `user-${i}`) }); // Override batch settings per execution // 🎯 TypeScript shows batch options for PATCH method const { execute: updateUsers } = useEnfyraApi('/users', { method: 'patch', batchSize: 20, // Available: PATCH method supports batching concurrent: 5 // Available: PATCH method supports batching }); // This execution uses different settings // 🎯 Batch options in execute() only available when using `ids` or `files` await updateUsers({ ids: largeUserList, // Triggers batch mode body: { status: 'active' }, batchSize: 50, // Available: Using `ids` parameter concurrent: 10, // Available: Using `ids` parameter onProgress: (progress) => { // Available: Using `ids` parameter console.log(`Updating: ${progress.completed}/${progress.total}`); } }); // Single operation - batch options NOT available in execute await updateUsers({ id: 'single-user-id', // Single operation, no batch options body: { status: 'active' } // batchSize: 50, // Not available: Not using `ids` or `files` // concurrent: 10, // Not available: Not using `ids` or `files` // onProgress: () => {} // Not available: Not using `ids` or `files` }); // Batch file upload with real-time progress tracking const progressState = ref({ progress: 0, completed: 0, total: 0, failed: 0, estimatedTimeRemaining: 0, operationsPerSecond: 0 }); // 🎯 TypeScript shows batch options for POST method (supports file uploads) const { execute: uploadFiles } = useEnfyraApi('/file_definition', { method: 'post', batchSize: 5, // Available: POST method supports batching for files concurrent: 2, // Available: POST method supports batching for files errorContext: 'Upload Files', onProgress: (progress) => { // Available: POST method supports batching progressState.value = progress; console.log(`Progress: ${progress.progress}% (${progress.completed}/${progress.total})`); console.log(`ETA: ${Math.round((progress.estimatedTimeRemaining || 0) / 1000)}s`); console.log(`Speed: ${progress.operationsPerSecond?.toFixed(1)} ops/sec`); } }); // Convert files to FormData array (matches enfyra_app pattern) const formDataArray = selectedFiles.map(file => { const formData = new FormData(); formData.append('file', file); formData.append('folder', folderId || 'null'); return formData; }); await uploadFiles({ files: formDataArray // Array of FormData objects }); // Real-time progress tracking with detailed results const { execute: processData } = useEnfyraApi('/process', { method: 'post', batchSize: 10, concurrent: 3, onProgress: (progress) => { // Display progress bar updateProgressBar(progress.progress); // Show detailed metrics console.log('Batch Progress:', { percentage: progress.progress, completed: progress.completed, total: progress.total, failed: progress.failed, currentBatch: progress.currentBatch, totalBatches: progress.totalBatches, averageTime: progress.averageTime, estimatedTimeRemaining: progress.estimatedTimeRemaining, operationsPerSecond: progress.operationsPerSecond }); // Handle individual results progress.results.forEach(result => { if (result.status === 'failed') { console.error(`Item ${result.index} failed:`, result.error); } }); } }); await processData({ ids: largeDataSet, body: processingOptions }); ``` ### Real-time Progress Interface ```typescript interface BatchProgress { progress: number; // 0-100 percentage completed: number; // Number of completed operations total: number; // Total number of operations failed: number; // Number of failed operations inProgress: number; // Operations currently running estimatedTimeRemaining?: number; // Milliseconds remaining averageTime?: number; // Average time per operation (ms) currentBatch: number; // Current batch being processed totalBatches: number; // Total number of batches operationsPerSecond?: number; // Processing speed results: Array<{ // Detailed results index: number; status: 'completed' | 'failed'; result?: any; error?: ApiError; duration?: number; }>; } ``` ### TypeScript Integration ```typescript // Define your API response types interface User { id: string; name: string; email: string; } interface ApiResponse<T> { data: T[]; meta: { totalCount: number }; } // Use with full type safety const { data } = useEnfyraApi<ApiResponse<User>>('/users', { ssr: true }); // TypeScript knows data.value is ApiResponse<User> | null const users = computed(() => data.value?.data || []); ``` ### Reactive Parameters ```typescript const searchQuery = ref(''); const page = ref(1); // SSR mode with reactive query (executes immediately) const { data, refresh } = useEnfyraApi('/users', { ssr: true, key: () => `users-${page.value}-${searchQuery.value}`, // Optional query: computed(() => ({ search: searchQuery.value, page: page.value, limit: 10 })) }); // Watch for changes and refresh watch([searchQuery, page], () => refresh()); ``` ## Documentation For comprehensive guides and examples: 📚 **[useEnfyraApi Complete Guide](https://github.com/dothinh115/enfyra-sdk-nuxt/blob/main/docs/useEnfyraApi.md)** - API client composable with SSR support, batch operations, and error handling 🔐 **[useEnfyraAuth Complete Guide](https://github.com/dothinh115/enfyra-sdk-nuxt/blob/main/docs/useEnfyraAuth.md)** - Authentication composable with user management and login/logout functionality Key topics covered: - SSR vs Client Mode comparison - Authentication and headers forwarding - Batch operations and CRUD patterns - User management and authentication flows - Error handling best practices - TypeScript integration - Performance optimization - Migration guides ## Configuration ### Module Options ```typescript // nuxt.config.ts export default defineNuxtConfig({ modules: ["@enfyra/sdk-nuxt"], enfyraSDK: { // Required: Main API URL apiUrl: process.env.ENFYRA_API_URL || "http://localhost:1105", // Required: App URL for SSR requests appUrl: process.env.ENFYRA_APP_URL || "http://localhost:3001", }, }) ``` ### Environment Variables ```bash # .env ENFYRA_API_URL=https://api.enfyra.com ENFYRA_APP_URL=https://app.enfyra.com ``` ## Best Practices ### 1. Choose the Right Mode ```typescript // Use SSR for initial page data (runs immediately) const { data } = useEnfyraApi('/dashboard', { ssr: true, key: 'dashboard' // Optional }); // Use Client mode for user interactions (manual execution) const { execute: saveData } = useEnfyraApi('/settings', { method: 'patch', errorContext: 'Save Settings' }); ``` ### 2. Proper Error Handling ```typescript // Check error state (don't use try-catch) async function handleSubmit() { await execute({ body: formData }); if (error.value) { return; // Error already logged } // Success handling toast.success('Saved successfully'); } ``` ### 3. Type Safety ```typescript // Define interfaces for API responses interface CreateUserResponse { data: User; message: string; } const { execute } = useEnfyraApi<CreateUserResponse>('/users', { method: 'post' }); ``` ## Troubleshooting ### Common Issues 1. **Headers not forwarded in SSR** - Ensure `ssr: true` is set 2. **Batch operations not working** - Only available in Client mode 3. **Data not reactive** - Use computed refs for reactive parameters 4. **TypeScript errors** - Check return type differences between modes ### Performance Tips - Use SSR for initial data loading (better SEO, faster page loads) - Use Client mode for user interactions (better UX) - Implement proper cache keys to avoid over-caching - Group related operations with batch APIs ## Development ### Testing The SDK includes a comprehensive test suite using Vitest: ```bash # Run tests once npm run test:run # Run tests in watch mode npm test # Run tests with UI npm run test:ui ``` **Test Coverage:** - **Extension naming utilities** - UUID generation and validation - **Vue SFC validation** - Syntax and structure validation - **JS bundle validation** - Syntax and export validation - **Extension processing** - Complete workflow testing - **35 test cases** covering all edge cases and error handling ### Building ```bash # Build the module npm run build # Development mode npm run dev ``` ## License MIT ## Contributing Pull requests are welcome! Please read our contributing guidelines and ensure tests pass before submitting. ## Changelog See [CHANGELOG.md](https://github.com/dothinh115/enfyra-sdk-nuxt/blob/main/CHANGELOG.md) for a detailed history of changes and migration guides. ## Support For issues and questions: - 📖 Check the [detailed documentation](https://github.com/dothinh115/enfyra-sdk-nuxt/blob/main/docs/useEnfyraApi.md) - 🐛 [Report bugs](https://github.com/dothinh115/enfyra-sdk-nuxt/issues) - 💬 [GitHub Discussions](https://github.com/dothinh115/enfyra-sdk-nuxt/discussions)