@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 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`, `statusCode`, and an optional `debugMessage` to ensure seamless integration with the library's error handling mechanisms.
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
- "statusCode": 400, // HTTP status code associated with the error
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
- ![GitHub License](https://img.shields.io/github/license/DCC-BS/communication.bs.js) [![Checked with Biome](https://img.shields.io/badge/Checked_with-Biome-60a5fa?style=flat&logo=biome)](https://biomejs.dev) ![NPM Version](https://img.shields.io/npm/v/%40dcc-bs%2Fcommunication.bs.js)
14
+ ![GitHub License](https://img.shields.io/github/license/DCC-BS/communication.bs.js)
15
+ [![Checked with Biome](https://img.shields.io/badge/Checked_with-Biome-60a5fa?style=flat&logo=biome)](https://biomejs.dev)
16
+ [![NPM Version](https://img.shields.io/npm/v/%40dcc-bs%2Fcommunication.bs.js)](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 # Core API fetch functions
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
- └── models/ # TypeScript models
223
- ├── api_error.ts
224
- ├── api_error_response.ts
225
- └── api_fetch_options.ts
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
- - `statusCode`: HTTP status code
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