Skip to content

enfyra/sdk-nuxt

Repository files navigation

@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

npm install @enfyra/sdk-nuxt

Setup

Add the module to your nuxt.config.ts:

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

// 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

// 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

<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:

<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.

// 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.

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

// 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

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

// 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

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 - API client composable with SSR support, batch operations, and error handling

πŸ” useEnfyraAuth Complete Guide - 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

// 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

# .env
ENFYRA_API_URL=https://api.enfyra.com
ENFYRA_APP_URL=https://app.enfyra.com

Best Practices

1. Choose the Right Mode

// βœ… 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

// βœ… 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

// βœ… 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:

# 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

# 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 for a detailed history of changes and migration guides.

Support

For issues and questions:

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published