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:
- Uses Real Storage: Data persists through our intelligent storage system
- Simulates API Interface: HTTP-like calls, validation, error handling
- Intelligent Caching: First call stores data, subsequent calls hit cache
- Zero HTTP Overhead: After initial cache, no additional network simulation
- Easy API Transition: When real API is ready, swap configuration only
- Realistic Testing: Network delays, server errors, validation failures
🧠 Why This Approach is Superior
Solid Foundation Strategy
- ✅ Everything goes through storage system - Consistent data management
- ✅ API-ready from day one - Zero refactoring when API launches
- ✅ Intelligent caching - One HTTP call, then pure storage performance
- ✅ Realistic development - Network simulation, error handling, validation
- ✅ Zero performance penalty - Cached data served at storage speed
📊 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
- Data persists using real storage (IndexedDB/OPFS)
- Interface matches future API exactly
- No data migration needed when switching
2. Realistic Development
- Network delays and errors
- Server-side validation simulation
- API response format testing
- Error handling development
3. Seamless Transition
- Change configuration only
- No code changes in theme system
- Gradual migration possible (some endpoints mock, others real)
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!