Smart Mock Api Pattern

Ecosistema completo para organización y gestión de contenido con interfaces espaciales

Smart Mock API Pattern for Future API Readiness

🎯 Concept: API-First Development with Intelligent Storage Foundation

This is the RECOMMENDED approach for all features in our NX monorepo. Instead of direct storage access, use HTTP calls through our intelligent mock API system that:

  1. Uses Real Storage: Data persists through our intelligent storage system
  2. Simulates API Interface: HTTP-like calls, validation, error handling
  3. Intelligent Caching: First call stores data, subsequent calls hit cache
  4. Zero HTTP Overhead: After initial cache, no additional network simulation
  5. Easy API Transition: When real API is ready, swap configuration only
  6. Realistic Testing: Network delays, server errors, validation failures

🧠 Why This Approach is Superior

Solid Foundation Strategy

📊 Storage Types Available

Your mock API system supports 5 storage types for different use cases:

Storage Type Performance Use Case Data Persistence
'intelligent' ⭐ 0.2ms cached Production, API caching ✅ Smart redundancy
'indexeddb' 2-10ms cached Legacy migration ✅ IndexedDB only
'localstorage' 1-5ms cached Debugging, testing ✅ localStorage only
'memory' 0.1ms cached Unit tests, demos ❌ Session only
'storage-core' 0.2ms cached Alias for intelligent ✅ Smart redundancy

When NOT to Use 'intelligent'

❌ Unit Testing → Use 'memory'

// Tests need clean state
persistence: { storage: 'memory', ttl: 0 }

❌ Demo Presentations → Use 'memory'

// Fresh data each demo session
persistence: { storage: 'memory', ttl: 3600000 }

❌ Storage Debugging → Use 'localstorage'

// Force specific adapter for isolation
persistence: { storage: 'localstorage', autoCleanup: false }

❌ Legacy Migration → Use 'indexeddb'

// Match old database structure temporarily
persistence: { 
  storage: 'indexeddb', 
  dbName: 'legacy-db',
  storeName: 'legacy-data' 
}

For 99% of Cases: Use 'intelligent' 🎯

// ✅ RECOMMENDED: Production configuration
persistence: {
  enabled: true,
  storage: 'intelligent',      // Automatic optimization
  namespace: 'your-app-api',   // Isolation
  ttl: 3600000,               // 1 hour cache
  autoCleanup: true           // Maintenance
}

🏗️ Smart Mock Architecture

// Smart Mock API that uses real storage underneath
class SmartMockApiStorage implements GgStorage {
  private realStorage: GgStorage;
  private apiConfig: {
    baseUrl: string;        // Future API base URL
    timeout: number;        // Simulate network timeout
    errorRate: number;      // Simulate API errors (0-1)
    validationRules: any;   // Server-side validation rules
  };
  
  constructor(config: GgStorageConfig, apiConfig: any) {
    // Use REAL storage implementation underneath
    this.realStorage = new CoreGgStorage(config);
    this.apiConfig = apiConfig;
  }
  
  // API-like interface that uses real storage
  async get<T>(key: string): Promise<T | null> {
    try {
      // 🔄 Simulate API behavior
      await this.simulateNetworkCall('GET', `/storage/${key}`);
      
      // ✅ Use real storage (data persists!)
      return await this.realStorage.get<T>(key);
      
    } catch (apiError) {
      // 🚨 Simulate API errors (network, server, etc.)
      throw new StorageApiError(apiError.message, apiError.code);
    }
  }
  
  async put<T>(key: string, value: T): Promise<boolean> {
    try {
      // 🔍 Simulate server-side validation
      const validation = await this.simulateServerValidation(key, value);
      if (!validation.success) {
        throw new StorageApiError(validation.error, 400);
      }
      
      // 🔄 Simulate API call with payload
      await this.simulateNetworkCall('POST', `/storage/${key}`, value);
      
      // ✅ Use real storage (data actually saves!)
      const result = await this.realStorage.put(key, value);
      
      // 📊 Simulate API response format
      return this.simulateApiResponse({ success: result, key, saved: true });
      
    } catch (apiError) {
      throw new StorageApiError(apiError.message, apiError.code);
    }
  }
  
  private async simulateNetworkCall(method: string, endpoint: string, data?: any): Promise<void> {
    // Simulate network delay (realistic 10-200ms)
    const delay = Math.random() * 190 + 10;
    await new Promise(resolve => setTimeout(resolve, delay));
    
    // Simulate network errors
    if (Math.random() < this.apiConfig.errorRate) {
      const errors = [
        { message: 'Network timeout', code: 408 },
        { message: 'Server unavailable', code: 503 },
        { message: 'Rate limit exceeded', code: 429 }
      ];
      const error = errors[Math.floor(Math.random() * errors.length)];
      throw error;
    }
    
    console.log(`🔄 Mock API: ${method} ${this.apiConfig.baseUrl}${endpoint}`, data ? { payload: data } : '');
  }
  
  private async simulateServerValidation(key: string, value: any): Promise<{success: boolean, error?: string}> {
    // Simulate server-side validation rules
    if (!key || key.length < 3) {
      return { success: false, error: 'Key must be at least 3 characters' };
    }
    
    if (!value) {
      return { success: false, error: 'Value cannot be empty' };
    }
    
    // Simulate theme-specific validation
    if (key.includes('theme') && typeof value === 'object') {
      if (!value.id || !value.metadata) {
        return { success: false, error: 'Invalid theme structure' };
      }
    }
    
    return { success: true };
  }
  
  private simulateApiResponse<T>(data: T): T {
    // Add API-like metadata
    const apiResponse = {
      ...data,
      timestamp: new Date().toISOString(),
      apiVersion: 'v1',
      server: 'mock-api'
    };
    
    return apiResponse as T;
  }
}

// Custom error class for API errors
class StorageApiError extends Error {
  constructor(message: string, public code: number) {
    super(message);
    this.name = 'StorageApiError';
  }
}

🔄 Easy Transition Pattern

When your real API is ready, swap the implementation:

// Development: Smart Mock
const createStorageWrapper = (config: GgStorageConfig): GgStorage => {
  const apiConfig = {
    baseUrl: 'https://api.yourdomain.com',  // Future API URL
    timeout: 5000,
    errorRate: 0.05,  // 5% error rate for testing
    validationRules: { /* your rules */ }
  };
  
  return new SmartMockApiStorage(config, apiConfig);
};

// Production: Real API
const createStorageWrapper = (config: GgStorageConfig): GgStorage => {
  return new RealApiStorage(config, {
    baseUrl: 'https://api.yourdomain.com',
    timeout: 5000,
    headers: { 'Authorization': 'Bearer your-token' }
  });
};

🎯 Benefits of This Approach

1. Real Data + API Interface

2. Realistic Development

3. Seamless Transition

4. Advanced Testing Scenarios

const testingConfig = {
  errorRate: 0.3,        // 30% errors for robust error handling
  networkDelay: [100, 2000],  // Variable delays
  serverValidation: true,      // Test validation logic
  offlineMode: false          // Test offline scenarios
};

🔧 Implementation Using Our Existing Mock API System

IMPORTANT: We already have a sophisticated mock API system at @mock-api. Use it directly instead of creating custom wrappers:

Step 1: Configure Mock API with Intelligent Storage

// In app.config.ts
import { mockHttpInterceptor, MockInterceptorUtilsService } from '@mock-api';
import { provideAppInitializer } from '@angular/core';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(withInterceptors([mockHttpInterceptor])),
    
    // Modern app initializer pattern
    provideAppInitializer(() => {
      const utils = inject(MockInterceptorUtilsService);
      return () => utils.configureMockInterceptor({
        enabled: !environment.production,
        fallbackToNetwork: false,
        persistence: {
          enabled: true,
          storage: 'intelligent',      // 🧠 Uses our intelligent storage!
          namespace: 'app-api-cache',  // Namespace for API responses
          ttl: 3600000,               // 1 hour TTL
          autoCleanup: true
        },
        delay: [50, 150]  // Realistic API delays
      }, {
        // Theme API mocks
        ...ThemeApiMocks,
        // Other feature mocks
        ...UserApiMocks
      });
    })
  ]
};

Step 2: Create Theme API Mock Definitions

// theme-api-mocks.ts
import { MockDefinition, MockRequest, MockResponse } from '@mock-api';

export const ThemeApiMocks: MockDefinition = {
  'GET /api/themes': (req: MockRequest): MockResponse => ({
    status: 200,
    data: [], // Will be populated from intelligent storage
    headers: { 'X-Cache-Source': 'intelligent-storage' }
  }),

  'POST /api/themes': (req: MockRequest): MockResponse => {
    const theme = req.body;
    // Mock API + intelligent storage handles persistence automatically
    return {
      status: 201,
      data: theme,
      headers: { 
        'Location': `/api/themes/${theme.id}`,
        'X-Saved-To': 'intelligent-storage'
      }
    };
  },

  'GET /api/themes/:id': (req: MockRequest): MockResponse => ({
    status: 200,
    data: null, // Will be loaded from intelligent storage
    headers: { 'X-Cache-Source': 'intelligent-storage' }
  }),

  'DELETE /api/themes/:id': (req: MockRequest): MockResponse => ({
    status: 204,
    data: null
  })
};

Step 3: Use HTTP Calls in Theme Service

// theme.service.ts
@Injectable({ providedIn: 'root' })
export class ThemeService {
  private http = inject(HttpClient);

  // ✅ RECOMMENDED: Use HTTP calls through intelligent mock API
  getAllThemes(): Observable<Theme[]> {
    // First call: HTTP → Mock API → Intelligent Storage → Cache
    // Subsequent calls: HTTP → Mock API → Cache (instant)
    return this.http.get<Theme[]>('/api/themes');
  }

  saveTheme(theme: Theme): Observable<Theme> {
    // Automatically cached by intelligent storage
    return this.http.post<Theme>('/api/themes', theme);
  }

  getTheme(id: string): Observable<Theme> {
    // Cached after first call
    return this.http.get<Theme>(`/api/themes/${id}`);
  }

  deleteTheme(id: string): Observable<void> {
    return this.http.delete<void>(`/api/themes/${id}`);
  }
}

🌟 Advanced Features

1. Offline/Online Simulation

class SmartMockApiStorage {
  private isOnline = navigator.onLine;
  
  async get(key: string) {
    if (!this.isOnline) {
      // Use cached data only
      return await this.realStorage.get(key);
    }
    
    // Full API simulation when "online"
    return await this.simulateApiCall(() => this.realStorage.get(key));
  }
}

2. API Versioning

const apiConfig = {
  version: 'v1',
  endpoints: {
    v1: { baseUrl: 'https://api.yourdomain.com/v1' },
    v2: { baseUrl: 'https://api.yourdomain.com/v2' }  // Future version
  }
};

3. Gradual Migration

class HybridApiStorage {
  async get(key: string) {
    if (key.startsWith('themes/')) {
      // Use real API for themes
      return await this.realApiStorage.get(key);
    } else {
      // Use mock for other data
      return await this.smartMockStorage.get(key);
    }
  }
}

🚀 Performance Characteristics

First Call (HTTP + Storage)

HTTP GET /api/themes
└── Mock API intercepts (10-50ms)
    └── Intelligent Storage lookup (2-20ms) 
        └── Cache miss → Store result
        └── Return data
Total: ~15-70ms (one-time cost)

Subsequent Calls (Pure Storage)

HTTP GET /api/themes
└── Mock API intercepts (0.1ms)
    └── Intelligent Storage cache hit (0.1ms)
        └── Return cached data instantly
Total: ~0.2ms (storage speed!)

🎯 Key Benefits for NX Monorepo

✅ Solid Foundation: All features use consistent HTTP + storage pattern
✅ API-Ready: Zero refactoring when real API launches
✅ Performance: One call to cache, then storage-speed access
✅ Realistic: Network simulation, errors, validation during development
✅ Intelligent: Automatic caching, adapter selection, redundancy
✅ Maintainable: Clear separation between API interface and storage

🔧 When Real API is Ready

Simply update the configuration:

// Development
utils.configureMockInterceptor({ enabled: true, ... }, mocks);

// Production  
utils.configureMockInterceptor({ 
  enabled: false,           // ← Disable mocking
  fallbackToNetwork: true   // ← Use real API
}, {});

No code changes needed in your services! 🎉

This pattern gives you the best of both worlds: real persistent data with API-ready architecture for seamless production transition!