@dcc-bs/communication.bs.js 0.0.4 → 0.1.0
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/README.md +312 -11
- package/dist/communication.bs.d.ts +527 -0
- package/dist/{communication.bs.js.js → communication.bs.js} +2534 -2277
- package/dist/communication.bs.umd.cjs +23 -0
- package/package.json +8 -6
- package/dist/communication.bs.js.umd.cjs +0 -23
package/README.md
CHANGED
|
@@ -2,28 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
A TypeScript library for type-safe API communication with built-in error handling, schema validation, and Vue composables. Designed for modern web applications that require robust HTTP request handling with automatic response parsing and validation.
|
|
4
4
|
|
|
5
|
-
This library expects the API to follow a consistent error response format to leverage its full capabilities. The error responses should include fields such as `errorId`, `
|
|
5
|
+
This library expects the API to follow a consistent error response format to leverage its full capabilities. The error responses should include fields such as `errorId`, `status`, and an optional `debugMessage` to ensure seamless integration with the library's error handling mechanisms.
|
|
6
6
|
```json
|
|
7
7
|
{
|
|
8
8
|
"errorId": "string", // Unique identifier for the error type
|
|
9
|
-
"
|
|
9
|
+
"status": 400, // HTTP status code associated with the error
|
|
10
10
|
"debugMessage": "string" // Optional detailed debug message for developers
|
|
11
11
|
}
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-

|
|
14
|
+

|
|
15
|
+
[](https://biomejs.dev)
|
|
16
|
+
[](https://www.npmjs.com/package/@dcc-bs/communication.bs.js)
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
## Features
|
|
18
20
|
|
|
19
|
-
- **Type-Safe API Requests**: Full TypeScript support with type inference
|
|
21
|
+
- **Type-Safe API Requests**: Full TypeScript support with type inference and overloaded function signatures
|
|
22
|
+
- **Factory Pattern**: Create multiple API clients with different configurations
|
|
20
23
|
- **Schema Validation**: Built-in Zod schema validation for API responses
|
|
24
|
+
- **Advanced Configuration**: Flexible fetcher builder with extensive configuration options
|
|
21
25
|
- **Error Handling**: Standardized API error responses with custom error types
|
|
22
|
-
- **Streaming Support**: Handle streaming API responses with ease
|
|
26
|
+
- **Streaming Support**: Handle streaming API responses with ease including async iteration
|
|
23
27
|
- **Vue Composables**: Ready-to-use Vue 3 composables for reactive API calls
|
|
24
28
|
- **Multiple Response Types**: Support for JSON, text, and streaming responses
|
|
29
|
+
- **Request Hooks**: Intercept requests with beforeRequest, afterResponse, and onError hooks
|
|
30
|
+
- **Automatic Retries**: Configurable retry logic with exponential backoff
|
|
31
|
+
- **Request Deduplication**: Prevent duplicate simultaneous requests automatically
|
|
32
|
+
- **Timeout Management**: Set request timeouts with automatic abort
|
|
33
|
+
- **Authentication**: Built-in support for Bearer tokens and Basic authentication
|
|
25
34
|
- **Abort Support**: Request cancellation with AbortController integration
|
|
26
35
|
- **FormData Support**: Automatic handling of multipart/form-data requests
|
|
36
|
+
- **Debug Mode**: Optional request/response logging for development
|
|
27
37
|
|
|
28
38
|
## Technology Stack
|
|
29
39
|
|
|
@@ -49,6 +59,48 @@ npm install @dcc-bs/communication.bs.js
|
|
|
49
59
|
pnpm add @dcc-bs/communication.bs.js
|
|
50
60
|
```
|
|
51
61
|
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
### Simple Usage (Recommended for most cases)
|
|
65
|
+
|
|
66
|
+
The library provides ready-to-use functions for immediate use:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { apiFetch, isApiError } from '@dcc-bs/communication.bs.js';
|
|
70
|
+
|
|
71
|
+
// Simple API call
|
|
72
|
+
const response = await apiFetch<{ message: string }>('/api/hello');
|
|
73
|
+
|
|
74
|
+
if (isApiError(response)) {
|
|
75
|
+
console.error('Error:', response.errorId);
|
|
76
|
+
} else {
|
|
77
|
+
console.log(response.message); // Fully typed!
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Advanced Usage with Factory Pattern
|
|
82
|
+
|
|
83
|
+
For advanced configuration, create custom API clients:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { createApiClient, createFetcherBuilder } from '@dcc-bs/communication.bs.js';
|
|
87
|
+
|
|
88
|
+
// Create a custom fetcher with configuration
|
|
89
|
+
const fetcher = createFetcherBuilder()
|
|
90
|
+
.setBaseURL('https://api.example.com')
|
|
91
|
+
.addHeader('X-API-Key', 'your-api-key')
|
|
92
|
+
.setRequestTimeout(5000)
|
|
93
|
+
.setRetries(3)
|
|
94
|
+
.enableDebug()
|
|
95
|
+
.build();
|
|
96
|
+
|
|
97
|
+
// Create an API client with the custom fetcher
|
|
98
|
+
const apiClient = createApiClient(fetcher);
|
|
99
|
+
|
|
100
|
+
// Use the client
|
|
101
|
+
const user = await apiClient.apiFetch('/users/1');
|
|
102
|
+
```
|
|
103
|
+
|
|
52
104
|
## Usage
|
|
53
105
|
|
|
54
106
|
### Basic API Fetch
|
|
@@ -173,6 +225,222 @@ await for(const text of textResults) {
|
|
|
173
225
|
}
|
|
174
226
|
```
|
|
175
227
|
|
|
228
|
+
## Advanced Configuration
|
|
229
|
+
|
|
230
|
+
The `createFetcherBuilder()` function provides a fluent API for configuring custom HTTP clients with advanced features.
|
|
231
|
+
|
|
232
|
+
### Base URL and Headers
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
import { createFetcherBuilder, createApiClient } from '@dcc-bs/communication.bs.js';
|
|
236
|
+
|
|
237
|
+
const fetcher = createFetcherBuilder()
|
|
238
|
+
.setBaseURL('https://api.example.com/v1')
|
|
239
|
+
.addHeader('X-API-Key', 'your-api-key')
|
|
240
|
+
.addHeader('X-Custom-Header', 'value')
|
|
241
|
+
.build();
|
|
242
|
+
|
|
243
|
+
const client = createApiClient(fetcher);
|
|
244
|
+
|
|
245
|
+
// Now all requests use the base URL and headers
|
|
246
|
+
const user = await client.apiFetch('/users/1'); // GET https://api.example.com/v1/users/1
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Authentication
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// Bearer Token Authentication
|
|
253
|
+
const fetcher = createFetcherBuilder()
|
|
254
|
+
.setBaseURL('https://api.example.com')
|
|
255
|
+
.setAuth({
|
|
256
|
+
type: 'bearer',
|
|
257
|
+
token: 'your-jwt-token'
|
|
258
|
+
})
|
|
259
|
+
.build();
|
|
260
|
+
|
|
261
|
+
// Basic Authentication
|
|
262
|
+
const fetcher = createFetcherBuilder()
|
|
263
|
+
.setAuth({
|
|
264
|
+
type: 'basic',
|
|
265
|
+
username: 'user',
|
|
266
|
+
password: 'pass'
|
|
267
|
+
})
|
|
268
|
+
.build();
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Retries and Timeouts
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
const fetcher = createFetcherBuilder()
|
|
275
|
+
.setBaseURL('https://api.example.com')
|
|
276
|
+
.setRequestTimeout(5000) // 5 second timeout
|
|
277
|
+
.setRetries(
|
|
278
|
+
3, // Max 3 retries
|
|
279
|
+
1000, // Initial delay in ms
|
|
280
|
+
[500, 502, 503, 504], // Retry on these status codes
|
|
281
|
+
)
|
|
282
|
+
.build();
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Request/Response Hooks
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
const fetcher = createFetcherBuilder()
|
|
289
|
+
.setBaseURL('https://api.example.com')
|
|
290
|
+
.setBeforeRequest((url, options) => {
|
|
291
|
+
console.log(`Sending request to: ${url}`);
|
|
292
|
+
})
|
|
293
|
+
.setAfterResponse((response) => {
|
|
294
|
+
// Process response after receiving
|
|
295
|
+
console.log(`Received response with status: ${response.status}`);
|
|
296
|
+
return response;
|
|
297
|
+
})
|
|
298
|
+
.setOnError((error) => {
|
|
299
|
+
console.error('Request failed:', error);
|
|
300
|
+
})
|
|
301
|
+
.build();
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Query Parameters and CORS
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
const fetcher = createFetcherBuilder()
|
|
308
|
+
.setBaseURL('https://api.example.com')
|
|
309
|
+
.setQueryParams({ version: '1.0', format: 'json' })
|
|
310
|
+
.setCredentials('include') // 'omit' | 'same-origin' | 'include'
|
|
311
|
+
.setMode('cors') // 'cors' | 'no-cors' | 'same-origin'
|
|
312
|
+
.setCache('no-cache') // any valid RequestCache value
|
|
313
|
+
.build();
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Request Deduplication
|
|
317
|
+
|
|
318
|
+
Prevent duplicate simultaneous requests to the same endpoint:
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
const fetcher = createFetcherBuilder()
|
|
322
|
+
.setBaseURL('https://api.example.com')
|
|
323
|
+
.enableDeduplication()
|
|
324
|
+
.build();
|
|
325
|
+
|
|
326
|
+
const client = createApiClient(fetcher);
|
|
327
|
+
|
|
328
|
+
// These three simultaneous requests will only trigger one actual HTTP request
|
|
329
|
+
const [user1, user2, user3] = await Promise.all([
|
|
330
|
+
client.apiFetch('/users/1'),
|
|
331
|
+
client.apiFetch('/users/1'),
|
|
332
|
+
client.apiFetch('/users/1')
|
|
333
|
+
]);
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Debug Mode
|
|
337
|
+
|
|
338
|
+
Enable detailed logging for development:
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
const fetcher = createFetcherBuilder()
|
|
342
|
+
.setBaseURL('https://api.example.com')
|
|
343
|
+
.enableDebug() // Logs all requests and responses
|
|
344
|
+
.build();
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Complete Example
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
import { createFetcherBuilder, createApiClient } from '@dcc-bs/communication.bs.js';
|
|
351
|
+
|
|
352
|
+
const fetcher = createFetcherBuilder()
|
|
353
|
+
.setBaseURL('https://api.example.com/v1')
|
|
354
|
+
.setAuth({ type: 'bearer', token: process.env.API_TOKEN })
|
|
355
|
+
.addHeader('X-App-Version', '1.0.0')
|
|
356
|
+
.setRequestTimeout(10000)
|
|
357
|
+
.setRetries({ maxRetries: 3, retryDelay: 1000 })
|
|
358
|
+
.setBeforeRequest((url, options) => {
|
|
359
|
+
console.log(`→ ${options.method || 'GET'} ${url}`);
|
|
360
|
+
return { url, options };
|
|
361
|
+
})
|
|
362
|
+
.setAfterResponse((response) => {
|
|
363
|
+
console.log(`← ${response.status} ${response.url}`);
|
|
364
|
+
return response;
|
|
365
|
+
})
|
|
366
|
+
.enableDeduplication()
|
|
367
|
+
.enableDebug()
|
|
368
|
+
.build();
|
|
369
|
+
|
|
370
|
+
const apiClient = createApiClient(fetcher);
|
|
371
|
+
|
|
372
|
+
export { apiClient };
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Framework Integration
|
|
376
|
+
|
|
377
|
+
### Nuxt 3 Plugin
|
|
378
|
+
|
|
379
|
+
Create a Nuxt plugin to provide the API client throughout your application:
|
|
380
|
+
|
|
381
|
+
**`plugins/api.ts`**:
|
|
382
|
+
```typescript
|
|
383
|
+
import { createFetcherBuilder, createApiClient } from '@dcc-bs/communication.bs.js';
|
|
384
|
+
|
|
385
|
+
export default defineNuxtPlugin(() => {
|
|
386
|
+
const config = useRuntimeConfig();
|
|
387
|
+
|
|
388
|
+
// Create a configured fetcher
|
|
389
|
+
const fetcher = createFetcherBuilder()
|
|
390
|
+
.setBaseURL(config.public.apiBaseUrl)
|
|
391
|
+
.setAuth({
|
|
392
|
+
type: 'bearer',
|
|
393
|
+
token: config.public.apiToken
|
|
394
|
+
})
|
|
395
|
+
.setRequestTimeout(10000)
|
|
396
|
+
.setRetries(2, 1000)
|
|
397
|
+
.enableDebug(!!import.meta.dev)
|
|
398
|
+
.build();
|
|
399
|
+
|
|
400
|
+
// Create the API client
|
|
401
|
+
const apiClient = createApiClient(fetcher);
|
|
402
|
+
|
|
403
|
+
// Provide the client to the app
|
|
404
|
+
return {
|
|
405
|
+
provide: {
|
|
406
|
+
api: apiClient
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
});
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
**Composable to access the API client**:
|
|
413
|
+
```ts
|
|
414
|
+
// composables/useApi.ts
|
|
415
|
+
export function useApi() {
|
|
416
|
+
const { $api } = useNuxtApp();
|
|
417
|
+
return $api;
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
**Usage in pages/components**:
|
|
422
|
+
```vue
|
|
423
|
+
<script setup lang="ts">
|
|
424
|
+
import { z } from 'zod';
|
|
425
|
+
|
|
426
|
+
const { apiFetch } = useApi();
|
|
427
|
+
|
|
428
|
+
const UserSchema = z.object({
|
|
429
|
+
id: z.number(),
|
|
430
|
+
name: z.string(),
|
|
431
|
+
email: z.string().email()
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// Use the provided API client
|
|
435
|
+
const response = await apiFetch('/users/1', {
|
|
436
|
+
schema: UserSchema
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
if (!isApiError(response)) {
|
|
440
|
+
console.log(response.name); // Fully typed!
|
|
441
|
+
}
|
|
442
|
+
</script>
|
|
443
|
+
```
|
|
176
444
|
## Development
|
|
177
445
|
|
|
178
446
|
### Setup
|
|
@@ -214,19 +482,32 @@ bun run check
|
|
|
214
482
|
|
|
215
483
|
```
|
|
216
484
|
src/
|
|
217
|
-
├── apiFetch.ts #
|
|
485
|
+
├── apiFetch.ts # Default API fetch function exports
|
|
486
|
+
├── apiFetchFactory.ts # API client factory
|
|
487
|
+
├── apiFetchUtils.ts # Utility functions (isApiError, error extraction)
|
|
488
|
+
├── fetcherFactory.ts # Fetcher builder with advanced configuration
|
|
218
489
|
├── index.ts # Main entry point
|
|
219
490
|
├── composables/ # Vue composables
|
|
220
491
|
│ ├── apiFetch.composable.ts
|
|
221
492
|
│ └── useApiFetchWithSchema.ts
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
493
|
+
├── types/ # TypeScript type definitions
|
|
494
|
+
│ ├── api_client.ts # ApiClient, ApiFetchFunction, ApiResponse types
|
|
495
|
+
│ ├── api_error.ts # ApiError class
|
|
496
|
+
│ ├── api_error_response.ts
|
|
497
|
+
│ ├── api_fetch_options.ts # Request options interfaces
|
|
498
|
+
│ ├── auth.ts # Authentication configuration types
|
|
499
|
+
│ ├── fetcher.ts # Fetcher function type
|
|
500
|
+
│ ├── fetcher_builder_options.ts
|
|
501
|
+
│ └── hooks.ts # Hook function types
|
|
502
|
+
└── utils/ # Utility modules
|
|
226
503
|
tests/ # Test files
|
|
227
504
|
├── apiFetch.test.ts
|
|
505
|
+
├── apiFetchFactory.test.ts
|
|
228
506
|
├── apiFetchTextMany.test.ts
|
|
507
|
+
├── apiFetchUtils.test.ts
|
|
229
508
|
├── apiStreamFetch.test.ts
|
|
509
|
+
├── fetcherFactory.test.ts
|
|
510
|
+
├── integration.test.ts
|
|
230
511
|
└── isApiError.test.ts
|
|
231
512
|
```
|
|
232
513
|
|
|
@@ -237,12 +518,32 @@ The library provides standardized error handling with predefined error types:
|
|
|
237
518
|
- `unexpected_error`: Unexpected API response format
|
|
238
519
|
- `fetch_failed`: Network or fetch operation failure
|
|
239
520
|
- `request_aborted`: Request was cancelled via AbortController
|
|
521
|
+
- `schema_validation_failed`: Response data doesn't match the provided Zod schema
|
|
240
522
|
|
|
241
523
|
All errors follow the `ApiError` structure with:
|
|
242
524
|
- `errorId`: Unique error identifier
|
|
243
|
-
- `
|
|
525
|
+
- `status`: HTTP status code
|
|
244
526
|
- `debugMessage`: Optional debug information
|
|
245
527
|
|
|
528
|
+
### Type Guard
|
|
529
|
+
|
|
530
|
+
Use the `isApiError()` function to check if a response is an error:
|
|
531
|
+
|
|
532
|
+
```typescript
|
|
533
|
+
import { apiFetch, isApiError } from '@dcc-bs/communication.bs.js';
|
|
534
|
+
|
|
535
|
+
const response = await apiFetch('/api/data');
|
|
536
|
+
|
|
537
|
+
if (isApiError(response)) {
|
|
538
|
+
// response is ApiError
|
|
539
|
+
console.error(`Error ${response.errorId}: ${response.debugMessage}`);
|
|
540
|
+
console.error(`Status: ${response.status}`);
|
|
541
|
+
} else {
|
|
542
|
+
// response is your data type
|
|
543
|
+
console.log(response);
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
246
547
|
## License
|
|
247
548
|
|
|
248
549
|
[MIT](LICENSE) © Data Competence Center Basel-Stadt
|