@fjell/registry 4.4.23 → 4.4.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js.map +1 -1
- package/docs/docs.config.ts +52 -2
- package/docs/index.html +9 -0
- package/docs/public/404.html +53 -0
- package/docs/public/client-api-overview.md +137 -0
- package/docs/public/configuration.md +759 -0
- package/docs/public/error-handling/README.md +356 -0
- package/docs/public/error-handling/network-errors.md +485 -0
- package/docs/public/operations/README.md +121 -0
- package/docs/public/operations/all.md +325 -0
- package/docs/public/operations/create.md +415 -0
- package/docs/public/operations/get.md +389 -0
- package/docs/timing-range.svg +41 -39
- package/package.json +1 -1
- package/vitest.config.ts +0 -6
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
# Network Errors and Timeouts
|
|
2
|
+
|
|
3
|
+
Handle connection failures, DNS issues, and request timeouts with automatic retry logic.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Network errors are among the most common issues in distributed systems. The Fjell Client API automatically handles these transient failures with intelligent retry logic and exponential backoff.
|
|
8
|
+
|
|
9
|
+
## Error Types
|
|
10
|
+
|
|
11
|
+
### NetworkError
|
|
12
|
+
- **Code**: `NETWORK_ERROR`
|
|
13
|
+
- **Retryable**: ✅ Yes
|
|
14
|
+
- **Common Causes**: Connection refused, DNS resolution failure, network unreachable
|
|
15
|
+
- **HTTP Equivalents**: Connection timeouts, DNS errors, network unavailable
|
|
16
|
+
|
|
17
|
+
### TimeoutError
|
|
18
|
+
- **Code**: `TIMEOUT_ERROR`
|
|
19
|
+
- **Retryable**: ✅ Yes
|
|
20
|
+
- **Common Causes**: Request takes longer than configured timeout
|
|
21
|
+
- **HTTP Equivalents**: Request timeout, gateway timeout
|
|
22
|
+
|
|
23
|
+
## Automatic Retry Behavior
|
|
24
|
+
|
|
25
|
+
Network errors are automatically retried with exponential backoff:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// This will automatically retry on network failures
|
|
29
|
+
try {
|
|
30
|
+
const user = await userApi.get(userKey);
|
|
31
|
+
console.log('Success (possibly after retries):', user.name);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
// Only reaches here after all retry attempts failed
|
|
34
|
+
console.error('Network completely unavailable:', error.message);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Default Retry Configuration
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
const defaultRetryConfig = {
|
|
42
|
+
maxRetries: 3, // Maximum retry attempts
|
|
43
|
+
initialDelayMs: 1000, // Initial delay: 1 second
|
|
44
|
+
maxDelayMs: 30000, // Maximum delay: 30 seconds
|
|
45
|
+
backoffMultiplier: 2, // Double delay each time
|
|
46
|
+
enableJitter: true // Add randomness to prevent thundering herd
|
|
47
|
+
};
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Retry Sequence Example
|
|
51
|
+
|
|
52
|
+
For a network error with default configuration:
|
|
53
|
+
|
|
54
|
+
1. **Initial attempt**: Fails with `ECONNREFUSED`
|
|
55
|
+
2. **Retry 1**: Wait ~1000ms (with jitter: 500-1000ms), retry
|
|
56
|
+
3. **Retry 2**: Wait ~2000ms (with jitter: 1000-2000ms), retry
|
|
57
|
+
4. **Retry 3**: Wait ~4000ms (with jitter: 2000-4000ms), retry
|
|
58
|
+
5. **Final failure**: If still failing, throw error
|
|
59
|
+
|
|
60
|
+
## Configuration
|
|
61
|
+
|
|
62
|
+
### Basic Network Resilience
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
const config = {
|
|
66
|
+
baseUrl: 'https://api.example.com',
|
|
67
|
+
retryConfig: {
|
|
68
|
+
maxRetries: 5, // More retries for critical operations
|
|
69
|
+
initialDelayMs: 500, // Faster initial retry
|
|
70
|
+
maxDelayMs: 60000, // Allow longer delays
|
|
71
|
+
enableJitter: true // Prevent retry storms
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const userApi = createPItemApi<User, 'user'>('user', ['users'], config);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Custom Network Error Handling
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const networkResilientConfig = {
|
|
82
|
+
baseUrl: 'https://api.example.com',
|
|
83
|
+
retryConfig: {
|
|
84
|
+
maxRetries: 3,
|
|
85
|
+
initialDelayMs: 1000,
|
|
86
|
+
maxDelayMs: 30000,
|
|
87
|
+
backoffMultiplier: 2,
|
|
88
|
+
enableJitter: true,
|
|
89
|
+
|
|
90
|
+
// Custom retry logic for network errors
|
|
91
|
+
shouldRetry: (error, attemptNumber) => {
|
|
92
|
+
// Always retry network and timeout errors
|
|
93
|
+
if (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT_ERROR') {
|
|
94
|
+
return attemptNumber < 5; // Allow up to 5 retries for network issues
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Default retry logic for other errors
|
|
98
|
+
return error.isRetryable && attemptNumber < 3;
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
// Monitor retry attempts
|
|
102
|
+
onRetry: (error, attemptNumber, delay) => {
|
|
103
|
+
console.log(`Network retry ${attemptNumber + 1}: ${error.message} (delay: ${delay}ms)`);
|
|
104
|
+
|
|
105
|
+
// Send metrics to monitoring service
|
|
106
|
+
metrics.increment('api.network.retry', {
|
|
107
|
+
error_code: error.code,
|
|
108
|
+
attempt: attemptNumber + 1
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Error Handling Patterns
|
|
116
|
+
|
|
117
|
+
### Basic Network Error Handling
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
async function fetchUserSafely(userKey: PriKey<'user'>): Promise<User | null> {
|
|
121
|
+
try {
|
|
122
|
+
return await userApi.get(userKey);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (error.code === 'NETWORK_ERROR') {
|
|
125
|
+
console.log('Network issue resolved automatically or exhausted retries');
|
|
126
|
+
|
|
127
|
+
// Could show user-friendly message
|
|
128
|
+
showNotification('Network connection issues. Please try again later.');
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (error.code === 'TIMEOUT_ERROR') {
|
|
133
|
+
console.log('Request timed out after retries');
|
|
134
|
+
|
|
135
|
+
// Could suggest checking connection
|
|
136
|
+
showNotification('Request timed out. Please check your connection.');
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Re-throw unexpected errors
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Graceful Degradation
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
async function getUserWithFallback(userKey: PriKey<'user'>): Promise<User | null> {
|
|
150
|
+
try {
|
|
151
|
+
// Try primary API with retry
|
|
152
|
+
return await userApi.get(userKey);
|
|
153
|
+
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT_ERROR') {
|
|
156
|
+
console.log('Primary API unavailable, trying alternatives...');
|
|
157
|
+
|
|
158
|
+
// Try cached data
|
|
159
|
+
try {
|
|
160
|
+
const cachedUser = await cache.get(`user:${userKey.pk}`);
|
|
161
|
+
if (cachedUser) {
|
|
162
|
+
console.log('Serving from cache');
|
|
163
|
+
return cachedUser;
|
|
164
|
+
}
|
|
165
|
+
} catch (cacheError) {
|
|
166
|
+
console.warn('Cache also unavailable:', cacheError.message);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Try backup API
|
|
170
|
+
try {
|
|
171
|
+
const backupUser = await backupUserApi.get(userKey);
|
|
172
|
+
console.log('Served from backup API');
|
|
173
|
+
return backupUser;
|
|
174
|
+
} catch (backupError) {
|
|
175
|
+
console.warn('Backup API also failed:', backupError.message);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// All sources failed
|
|
179
|
+
console.error('All data sources unavailable');
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Non-network errors
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Circuit Breaker Pattern
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
class CircuitBreaker {
|
|
193
|
+
private failures = 0;
|
|
194
|
+
private lastFailureTime = 0;
|
|
195
|
+
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
|
|
196
|
+
|
|
197
|
+
constructor(
|
|
198
|
+
private failureThreshold = 5,
|
|
199
|
+
private recoveryTimeMs = 30000
|
|
200
|
+
) {}
|
|
201
|
+
|
|
202
|
+
async execute<T>(operation: () => Promise<T>): Promise<T> {
|
|
203
|
+
if (this.state === 'OPEN') {
|
|
204
|
+
if (Date.now() - this.lastFailureTime > this.recoveryTimeMs) {
|
|
205
|
+
this.state = 'HALF_OPEN';
|
|
206
|
+
} else {
|
|
207
|
+
throw new Error('Circuit breaker is OPEN');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const result = await operation();
|
|
213
|
+
|
|
214
|
+
// Success - reset circuit breaker
|
|
215
|
+
if (this.state === 'HALF_OPEN') {
|
|
216
|
+
this.state = 'CLOSED';
|
|
217
|
+
this.failures = 0;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return result;
|
|
221
|
+
|
|
222
|
+
} catch (error) {
|
|
223
|
+
this.failures++;
|
|
224
|
+
this.lastFailureTime = Date.now();
|
|
225
|
+
|
|
226
|
+
if (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT_ERROR') {
|
|
227
|
+
if (this.failures >= this.failureThreshold) {
|
|
228
|
+
this.state = 'OPEN';
|
|
229
|
+
console.log('Circuit breaker opened due to network failures');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Usage
|
|
239
|
+
const circuitBreaker = new CircuitBreaker(3, 10000); // 3 failures, 10s recovery
|
|
240
|
+
|
|
241
|
+
async function resilientApiCall(userKey: PriKey<'user'>): Promise<User | null> {
|
|
242
|
+
try {
|
|
243
|
+
return await circuitBreaker.execute(() => userApi.get(userKey));
|
|
244
|
+
} catch (error) {
|
|
245
|
+
if (error.message === 'Circuit breaker is OPEN') {
|
|
246
|
+
console.log('Circuit breaker preventing calls, using fallback');
|
|
247
|
+
return await getFallbackUser(userKey);
|
|
248
|
+
}
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Monitoring and Observability
|
|
255
|
+
|
|
256
|
+
### Network Error Metrics
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
const networkMetrics = {
|
|
260
|
+
// Track network error rates
|
|
261
|
+
recordNetworkError: (error: NetworkError, context: any) => {
|
|
262
|
+
metrics.increment('api.network.errors', {
|
|
263
|
+
error_type: error.code,
|
|
264
|
+
endpoint: context.url,
|
|
265
|
+
method: context.method
|
|
266
|
+
});
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
// Track retry success rates
|
|
270
|
+
recordRetrySuccess: (attemptNumber: number, context: any) => {
|
|
271
|
+
metrics.increment('api.network.retry_success', {
|
|
272
|
+
attempt_number: attemptNumber,
|
|
273
|
+
endpoint: context.url
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
metrics.timing('api.network.recovery_time', context.duration, {
|
|
277
|
+
attempts: attemptNumber
|
|
278
|
+
});
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
// Track connection health
|
|
282
|
+
recordConnectionHealth: (isHealthy: boolean) => {
|
|
283
|
+
metrics.gauge('api.network.health', isHealthy ? 1 : 0);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Custom error handler with metrics
|
|
288
|
+
const config = {
|
|
289
|
+
errorHandler: (error, context) => {
|
|
290
|
+
if (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT_ERROR') {
|
|
291
|
+
networkMetrics.recordNetworkError(error, context);
|
|
292
|
+
|
|
293
|
+
// Alert on high network error rates
|
|
294
|
+
if (context.totalAttempts >= 3) {
|
|
295
|
+
alerting.sendAlert({
|
|
296
|
+
severity: 'warning',
|
|
297
|
+
message: `High network error rate: ${error.message}`,
|
|
298
|
+
context
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Health Checks
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
async function performHealthCheck(): Promise<boolean> {
|
|
310
|
+
try {
|
|
311
|
+
// Simple health check endpoint
|
|
312
|
+
await healthApi.get({ keyType: 'health', pk: 'status' });
|
|
313
|
+
networkMetrics.recordConnectionHealth(true);
|
|
314
|
+
return true;
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT_ERROR') {
|
|
317
|
+
networkMetrics.recordConnectionHealth(false);
|
|
318
|
+
console.warn('Health check failed:', error.message);
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// API is reachable but returned an error
|
|
323
|
+
networkMetrics.recordConnectionHealth(true);
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Run health checks periodically
|
|
329
|
+
setInterval(performHealthCheck, 30000); // Every 30 seconds
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Testing Network Errors
|
|
333
|
+
|
|
334
|
+
### Unit Testing
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
describe('Network Error Handling', () => {
|
|
338
|
+
it('should retry on network errors', async () => {
|
|
339
|
+
const mockApi = createMockApi();
|
|
340
|
+
|
|
341
|
+
// First call fails, second succeeds
|
|
342
|
+
mockApi.httpGet
|
|
343
|
+
.mockRejectedValueOnce(new NetworkError('ECONNREFUSED'))
|
|
344
|
+
.mockResolvedValueOnce({ id: 'user-123' });
|
|
345
|
+
|
|
346
|
+
const user = await userApi.get(userKey);
|
|
347
|
+
|
|
348
|
+
expect(user.id).toBe('user-123');
|
|
349
|
+
expect(mockApi.httpGet).toHaveBeenCalledTimes(2);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('should respect max retries', async () => {
|
|
353
|
+
const mockApi = createMockApi();
|
|
354
|
+
mockApi.httpGet.mockRejectedValue(new NetworkError('ECONNREFUSED'));
|
|
355
|
+
|
|
356
|
+
const config = { retryConfig: { maxRetries: 2 } };
|
|
357
|
+
const api = createPItemApi<User, 'user'>('user', ['users'], config);
|
|
358
|
+
|
|
359
|
+
await expect(api.get(userKey)).rejects.toThrow('ECONNREFUSED');
|
|
360
|
+
expect(mockApi.httpGet).toHaveBeenCalledTimes(3); // 1 initial + 2 retries
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('should apply exponential backoff', async () => {
|
|
364
|
+
const delays: number[] = [];
|
|
365
|
+
const mockSetTimeout = jest.spyOn(global, 'setTimeout').mockImplementation((fn, delay) => {
|
|
366
|
+
delays.push(delay);
|
|
367
|
+
return setTimeout(fn, 0); // Execute immediately for testing
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
const mockApi = createMockApi();
|
|
371
|
+
mockApi.httpGet.mockRejectedValue(new NetworkError('ECONNREFUSED'));
|
|
372
|
+
|
|
373
|
+
try {
|
|
374
|
+
await userApi.get(userKey);
|
|
375
|
+
} catch (error) {
|
|
376
|
+
// Expected to fail after retries
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
expect(delays).toHaveLength(3);
|
|
380
|
+
expect(delays[1]).toBeGreaterThan(delays[0]); // Exponential increase
|
|
381
|
+
expect(delays[2]).toBeGreaterThan(delays[1]);
|
|
382
|
+
|
|
383
|
+
mockSetTimeout.mockRestore();
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Integration Testing
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
describe('Network Resilience Integration', () => {
|
|
392
|
+
it('should handle intermittent network issues', async () => {
|
|
393
|
+
let callCount = 0;
|
|
394
|
+
|
|
395
|
+
server.use(
|
|
396
|
+
rest.get('/api/users/:id', (req, res, ctx) => {
|
|
397
|
+
callCount++;
|
|
398
|
+
|
|
399
|
+
// Fail first two calls, succeed on third
|
|
400
|
+
if (callCount <= 2) {
|
|
401
|
+
return res.networkError('Connection failed');
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return res(ctx.json({ id: 'user-123' }));
|
|
405
|
+
})
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
const user = await userApi.get(userKey);
|
|
409
|
+
expect(user.id).toBe('user-123');
|
|
410
|
+
expect(callCount).toBe(3);
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
## Best Practices
|
|
416
|
+
|
|
417
|
+
### 1. **Configure Appropriate Timeouts**
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
const config = {
|
|
421
|
+
baseUrl: 'https://api.example.com',
|
|
422
|
+
timeout: 10000, // 10 second timeout
|
|
423
|
+
retryConfig: {
|
|
424
|
+
maxRetries: 3,
|
|
425
|
+
initialDelayMs: 1000,
|
|
426
|
+
maxDelayMs: 15000
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### 2. **Implement Progressive Degradation**
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
async function getDataWithDegradation(key: string) {
|
|
435
|
+
try {
|
|
436
|
+
// Try full data
|
|
437
|
+
return await api.getFullData(key);
|
|
438
|
+
} catch (error) {
|
|
439
|
+
if (error.code === 'NETWORK_ERROR') {
|
|
440
|
+
try {
|
|
441
|
+
// Try summary data (smaller payload)
|
|
442
|
+
return await api.getSummaryData(key);
|
|
443
|
+
} catch (summaryError) {
|
|
444
|
+
// Use cached minimal data
|
|
445
|
+
return await cache.getMinimalData(key);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
throw error;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### 3. **User Experience Considerations**
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
async function fetchDataWithUI(key: string) {
|
|
457
|
+
const loadingIndicator = showLoadingSpinner();
|
|
458
|
+
|
|
459
|
+
try {
|
|
460
|
+
const data = await api.getData(key);
|
|
461
|
+
hideLoadingSpinner(loadingIndicator);
|
|
462
|
+
return data;
|
|
463
|
+
|
|
464
|
+
} catch (error) {
|
|
465
|
+
hideLoadingSpinner(loadingIndicator);
|
|
466
|
+
|
|
467
|
+
if (error.code === 'NETWORK_ERROR') {
|
|
468
|
+
showNotification('Connection issue. Retrying automatically...', 'info');
|
|
469
|
+
|
|
470
|
+
// Give user option to retry manually
|
|
471
|
+
showRetryButton(() => fetchDataWithUI(key));
|
|
472
|
+
} else if (error.code === 'TIMEOUT_ERROR') {
|
|
473
|
+
showNotification('Request timed out. Please try again.', 'warning');
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
throw error;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## Related Documentation
|
|
482
|
+
|
|
483
|
+
- [Error Handling Overview](./README.md) - Complete error handling guide
|
|
484
|
+
- [Server Errors](./server-errors.md) - Handle 5xx server errors
|
|
485
|
+
- [Configuration](../configuration.md) - Configure retry behavior
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# API Operations
|
|
2
|
+
|
|
3
|
+
This section provides comprehensive documentation for all available operations in the Fjell Client API. Each operation supports both Primary Items (PItemApi) and Contained Items (CItemApi) with location-aware variants.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
### Core CRUD Operations
|
|
8
|
+
- **[`all`](./all.md)** - Retrieve multiple items with query support
|
|
9
|
+
- **[`create`](./create.md)** - Create new items with validation
|
|
10
|
+
- **[`get`](./get.md)** - Retrieve single items by key
|
|
11
|
+
- **[`update`](./update.md)** - Update existing items with partial data
|
|
12
|
+
- **[`remove`](./remove.md)** - Delete items safely
|
|
13
|
+
- **[`one`](./one.md)** - Find single item with query conditions
|
|
14
|
+
|
|
15
|
+
### Business Logic Operations
|
|
16
|
+
- **[`action`](./action.md)** - Execute business actions on items
|
|
17
|
+
- **[`allAction`](./allAction.md)** - Execute batch actions on collections
|
|
18
|
+
- **[`facet`](./facet.md)** - Retrieve analytics and computed data for items
|
|
19
|
+
- **[`allFacet`](./allFacet.md)** - Retrieve analytics for collections
|
|
20
|
+
|
|
21
|
+
### Query Operations
|
|
22
|
+
- **[`find`](./find.md)** - Find items using custom finders
|
|
23
|
+
- **[`findOne`](./findOne.md)** - Find single item using custom finders
|
|
24
|
+
|
|
25
|
+
## Operation Categories
|
|
26
|
+
|
|
27
|
+
### Primary Item Operations (PItemApi)
|
|
28
|
+
Operations for independent entities that exist at the top level of your API hierarchy:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
const userApi = createPItemApi<User, 'user'>('user', ['users'], config);
|
|
32
|
+
|
|
33
|
+
// Basic CRUD
|
|
34
|
+
const users = await userApi.all({ active: true });
|
|
35
|
+
const user = await userApi.create({ name: 'John', email: 'john@example.com' });
|
|
36
|
+
const foundUser = await userApi.get(userKey);
|
|
37
|
+
const updatedUser = await userApi.update(userKey, { name: 'John Smith' });
|
|
38
|
+
await userApi.remove(userKey);
|
|
39
|
+
|
|
40
|
+
// Business logic
|
|
41
|
+
await userApi.action(userKey, 'activate', { reason: 'manual' });
|
|
42
|
+
const analytics = await userApi.facet(userKey, 'activity-stats');
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Contained Item Operations (CItemApi)
|
|
46
|
+
Operations for hierarchical entities that require location context:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
const taskApi = createCItemApi<Task, 'task', 'user'>('task', ['users', 'tasks'], config);
|
|
50
|
+
|
|
51
|
+
// Location-aware CRUD
|
|
52
|
+
const userTasks = await taskApi.all({ status: 'pending' }, [userId]);
|
|
53
|
+
const newTask = await taskApi.create({ title: 'Complete project' }, [userId]);
|
|
54
|
+
|
|
55
|
+
// Location-aware business logic
|
|
56
|
+
await taskApi.allAction('bulk-update', { priority: 'high' }, [userId]);
|
|
57
|
+
const taskMetrics = await taskApi.allFacet('completion-metrics', {}, [userId]);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Error Handling
|
|
61
|
+
|
|
62
|
+
All operations include comprehensive error handling with:
|
|
63
|
+
- **Automatic retry logic** for transient failures
|
|
64
|
+
- **Custom error types** for different scenarios
|
|
65
|
+
- **Enhanced error context** for debugging
|
|
66
|
+
- **Configurable retry behavior**
|
|
67
|
+
|
|
68
|
+
See the [Error Handling Documentation](../error-handling/README.md) for complete details.
|
|
69
|
+
|
|
70
|
+
## Performance Considerations
|
|
71
|
+
|
|
72
|
+
### Pagination
|
|
73
|
+
Use `limit` and `offset` in queries for large datasets:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const users = await userApi.all({
|
|
77
|
+
limit: 50,
|
|
78
|
+
offset: 100,
|
|
79
|
+
sort: 'createdAt:desc'
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Batch Operations
|
|
84
|
+
Use `allAction` and `allFacet` for efficient batch processing:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// Efficient batch update
|
|
88
|
+
await userApi.allAction('bulk-update', {
|
|
89
|
+
lastLoginAt: new Date()
|
|
90
|
+
}, { filter: 'active:true' });
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Caching Strategies
|
|
94
|
+
Implement client-side caching for frequently accessed data:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const cachedUser = await cache.get(userKey) || await userApi.get(userKey);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Type Safety
|
|
101
|
+
|
|
102
|
+
All operations are fully typed with TypeScript generics:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
interface User extends Item<'user'> {
|
|
106
|
+
name: string;
|
|
107
|
+
email: string;
|
|
108
|
+
active: boolean;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Type-safe operations
|
|
112
|
+
const userApi = createPItemApi<User, 'user'>('user', ['users'], config);
|
|
113
|
+
const user: User = await userApi.create({ name: 'John', email: 'john@example.com' });
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Next Steps
|
|
117
|
+
|
|
118
|
+
- Review individual operation documentation for detailed examples
|
|
119
|
+
- Check the [Configuration Guide](../configuration.md) for setup options
|
|
120
|
+
- Explore [Error Handling](../error-handling/README.md) for resilience patterns
|
|
121
|
+
- See [Examples](../../examples-README.md) for complete usage scenarios
|