@ioka-technologies/asyncapi-ts-client-template 0.0.7

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/USAGE.md ADDED
@@ -0,0 +1,586 @@
1
+ # TypeScript AsyncAPI Client Generator - Usage Guide
2
+
3
+ This guide demonstrates how to use the TypeScript AsyncAPI client generator template to create fully-typed, production-ready clients that are compatible with rust-asyncapi servers.
4
+
5
+ ## 🚀 Quick Start
6
+
7
+ ### 1. Install AsyncAPI CLI
8
+
9
+ ```bash
10
+ npm install -g @asyncapi/cli
11
+ ```
12
+
13
+ ### 2. Generate Your Client
14
+
15
+ ```bash
16
+ # Generate from your AsyncAPI specification
17
+ asyncapi generate fromTemplate your-api.yaml ./template -o ./my-client
18
+
19
+ # Or use the published template (when available)
20
+ # asyncapi generate fromTemplate your-api.yaml @asyncapi/typescript-template -o ./my-client
21
+ ```
22
+
23
+ ### 3. Install and Build
24
+
25
+ ```bash
26
+ cd my-client
27
+ npm install
28
+ npm run build
29
+ ```
30
+
31
+ ## 📋 Example AsyncAPI Specification
32
+
33
+ Here's an example AsyncAPI specification that demonstrates the features supported by this template:
34
+
35
+ ```yaml
36
+ asyncapi: 3.0.0
37
+ info:
38
+ title: Example API
39
+ version: 1.0.0
40
+ description: An example AsyncAPI for demonstrating the TypeScript client generator
41
+
42
+ servers:
43
+ websocket:
44
+ host: localhost:8080
45
+ protocol: ws
46
+ description: WebSocket server
47
+ http:
48
+ host: localhost:8080
49
+ protocol: http
50
+ description: HTTP server
51
+
52
+ channels:
53
+ user/profile:
54
+ address: user/profile
55
+ messages:
56
+ getUserProfile:
57
+ $ref: '#/components/messages/GetUserProfile'
58
+ userProfile:
59
+ $ref: '#/components/messages/UserProfile'
60
+
61
+ user/create:
62
+ address: user/create
63
+ messages:
64
+ createUser:
65
+ $ref: '#/components/messages/CreateUser'
66
+ userCreated:
67
+ $ref: '#/components/messages/UserCreated'
68
+
69
+ operations:
70
+ getUserProfile:
71
+ action: send
72
+ channel:
73
+ $ref: '#/channels/user~1profile'
74
+ messages:
75
+ - $ref: '#/channels/user~1profile/messages/getUserProfile'
76
+ reply:
77
+ channel:
78
+ $ref: '#/channels/user~1profile'
79
+ messages:
80
+ - $ref: '#/channels/user~1profile/messages/userProfile'
81
+
82
+ createUser:
83
+ action: send
84
+ channel:
85
+ $ref: '#/channels/user~1create'
86
+ messages:
87
+ - $ref: '#/channels/user~1create/messages/createUser'
88
+ reply:
89
+ channel:
90
+ $ref: '#/channels/user~1create'
91
+ messages:
92
+ - $ref: '#/channels/user~1create/messages/userCreated'
93
+
94
+ components:
95
+ messages:
96
+ GetUserProfile:
97
+ payload:
98
+ type: object
99
+ properties:
100
+ userId:
101
+ type: string
102
+ description: The user ID to fetch
103
+ required:
104
+ - userId
105
+
106
+ UserProfile:
107
+ payload:
108
+ type: object
109
+ properties:
110
+ id:
111
+ type: string
112
+ name:
113
+ type: string
114
+ email:
115
+ type: string
116
+ createdAt:
117
+ type: string
118
+ format: date-time
119
+ required:
120
+ - id
121
+ - name
122
+ - email
123
+ - createdAt
124
+
125
+ CreateUser:
126
+ payload:
127
+ type: object
128
+ properties:
129
+ name:
130
+ type: string
131
+ email:
132
+ type: string
133
+ required:
134
+ - name
135
+ - email
136
+
137
+ UserCreated:
138
+ payload:
139
+ type: object
140
+ properties:
141
+ id:
142
+ type: string
143
+ name:
144
+ type: string
145
+ email:
146
+ type: string
147
+ createdAt:
148
+ type: string
149
+ format: date-time
150
+ required:
151
+ - id
152
+ - name
153
+ - email
154
+ - createdAt
155
+ ```
156
+
157
+ ## 🔧 Generated Client Usage
158
+
159
+ ### WebSocket Client Example
160
+
161
+ ```typescript
162
+ import { ExampleApiClient } from './my-client';
163
+
164
+ async function websocketExample() {
165
+ // Create client with WebSocket transport
166
+ const client = new ExampleApiClient({
167
+ transport: 'websocket',
168
+ websocket: {
169
+ url: 'ws://localhost:8080',
170
+ reconnect: true,
171
+ reconnectInterval: 5000,
172
+ maxReconnectAttempts: 5,
173
+ timeout: 10000,
174
+ auth: {
175
+ token: 'your-jwt-token'
176
+ // or apiKey: 'your-api-key'
177
+ // or authorization: 'Bearer custom-token'
178
+ }
179
+ }
180
+ });
181
+
182
+ try {
183
+ // Connect to the server
184
+ await client.connect();
185
+ console.log('✅ Connected to WebSocket server');
186
+
187
+ // Send a request and get a typed response
188
+ const userProfile = await client.getUserProfile({
189
+ userId: '123'
190
+ });
191
+
192
+ // TypeScript provides full type safety
193
+ console.log(`User: ${userProfile.name} (${userProfile.email})`);
194
+ console.log(`Created: ${userProfile.createdAt}`);
195
+
196
+ // Create a new user
197
+ const newUser = await client.createUser({
198
+ name: 'John Doe',
199
+ email: 'john@example.com'
200
+ });
201
+
202
+ console.log(`Created user with ID: ${newUser.id}`);
203
+
204
+ } catch (error) {
205
+ console.error('Error:', error);
206
+ } finally {
207
+ // Always disconnect when done
208
+ await client.disconnect();
209
+ }
210
+ }
211
+
212
+ websocketExample();
213
+ ```
214
+
215
+ ### HTTP Client Example
216
+
217
+ ```typescript
218
+ import { ExampleApiClient } from './my-client';
219
+
220
+ async function httpExample() {
221
+ // Create client with HTTP transport
222
+ const client = new ExampleApiClient({
223
+ transport: 'http',
224
+ http: {
225
+ baseUrl: 'http://localhost:8080',
226
+ timeout: 30000,
227
+ auth: {
228
+ token: 'your-jwt-token'
229
+ },
230
+ retry: {
231
+ attempts: 3,
232
+ delay: 1000,
233
+ backoff: 'exponential',
234
+ maxDelay: 10000
235
+ }
236
+ }
237
+ });
238
+
239
+ try {
240
+ // Connect (for HTTP this just validates config)
241
+ await client.connect();
242
+ console.log('✅ HTTP client ready');
243
+
244
+ // Send requests with automatic retry
245
+ const userProfile = await client.getUserProfile({
246
+ userId: '456'
247
+ });
248
+
249
+ console.log(`User: ${userProfile.name} (${userProfile.email})`);
250
+
251
+ // Create a new user
252
+ const newUser = await client.createUser({
253
+ name: 'Jane Smith',
254
+ email: 'jane@example.com'
255
+ });
256
+
257
+ console.log(`Created user with ID: ${newUser.id}`);
258
+
259
+ } catch (error) {
260
+ console.error('Error:', error);
261
+ }
262
+ }
263
+
264
+ httpExample();
265
+ ```
266
+
267
+ ### Event Handling
268
+
269
+ ```typescript
270
+ import { ExampleApiClient } from './my-client';
271
+
272
+ async function eventHandlingExample() {
273
+ const client = new ExampleApiClient({
274
+ transport: 'websocket',
275
+ websocket: {
276
+ url: 'ws://localhost:8080',
277
+ reconnect: true
278
+ }
279
+ });
280
+
281
+ // Connection events
282
+ client.on('connected', () => {
283
+ console.log('🔌 Connected to server');
284
+ });
285
+
286
+ client.on('disconnected', (reason) => {
287
+ console.log('🔌 Disconnected:', reason);
288
+ });
289
+
290
+ client.on('reconnecting', (attempt) => {
291
+ console.log(`🔄 Reconnecting... attempt ${attempt}`);
292
+ });
293
+
294
+ client.on('error', (error) => {
295
+ console.error('❌ Client error:', error);
296
+ });
297
+
298
+ // Raw message events (for debugging)
299
+ client.on('message', (envelope) => {
300
+ console.log('📨 Raw message:', envelope);
301
+ });
302
+
303
+ await client.connect();
304
+
305
+ // Your application logic here...
306
+
307
+ await client.disconnect();
308
+ }
309
+
310
+ eventHandlingExample();
311
+ ```
312
+
313
+ ### Error Handling
314
+
315
+ ```typescript
316
+ import {
317
+ ExampleApiClient,
318
+ ConnectionError,
319
+ MessageTimeoutError,
320
+ HttpError,
321
+ ConfigurationError
322
+ } from './my-client';
323
+
324
+ async function errorHandlingExample() {
325
+ const client = new ExampleApiClient({
326
+ transport: 'websocket',
327
+ websocket: {
328
+ url: 'ws://localhost:8080'
329
+ }
330
+ });
331
+
332
+ try {
333
+ await client.connect();
334
+
335
+ const result = await client.getUserProfile({
336
+ userId: 'invalid-id'
337
+ });
338
+
339
+ } catch (error) {
340
+ if (error instanceof ConnectionError) {
341
+ console.error('Connection failed:', error.message);
342
+ // Handle connection issues
343
+
344
+ } else if (error instanceof MessageTimeoutError) {
345
+ console.error('Request timed out:', error.message);
346
+ // Handle timeout
347
+
348
+ } else if (error instanceof HttpError) {
349
+ console.error(`HTTP error ${error.status}: ${error.message}`);
350
+ // Handle HTTP errors
351
+
352
+ } else if (error instanceof ConfigurationError) {
353
+ console.error('Configuration error:', error.message);
354
+ // Handle config issues
355
+
356
+ } else {
357
+ console.error('Unknown error:', error);
358
+ // Handle unexpected errors
359
+ }
360
+ }
361
+ }
362
+
363
+ errorHandlingExample();
364
+ ```
365
+
366
+ ### Advanced Configuration
367
+
368
+ ```typescript
369
+ import { ExampleApiClient } from './my-client';
370
+
371
+ // Advanced WebSocket configuration
372
+ const wsClient = new ExampleApiClient({
373
+ transport: 'websocket',
374
+ websocket: {
375
+ url: 'wss://api.example.com',
376
+ reconnect: true,
377
+ reconnectInterval: 5000,
378
+ maxReconnectAttempts: 10,
379
+ timeout: 15000,
380
+ auth: {
381
+ headers: {
382
+ 'X-API-Key': 'your-api-key',
383
+ 'X-Client-Version': '1.0.0'
384
+ }
385
+ }
386
+ }
387
+ });
388
+
389
+ // Advanced HTTP configuration
390
+ const httpClient = new ExampleApiClient({
391
+ transport: 'http',
392
+ http: {
393
+ baseUrl: 'https://api.example.com',
394
+ timeout: 60000,
395
+ auth: {
396
+ token: 'jwt-token'
397
+ },
398
+ retry: {
399
+ attempts: 5,
400
+ delay: 2000,
401
+ backoff: 'exponential',
402
+ maxDelay: 30000,
403
+ retryCondition: (error) => {
404
+ // Custom retry logic
405
+ return error.status >= 500 || error.status === 429;
406
+ }
407
+ },
408
+ headers: {
409
+ 'User-Agent': 'MyApp/1.0.0',
410
+ 'Accept': 'application/json'
411
+ }
412
+ }
413
+ });
414
+ ```
415
+
416
+ ## 🧪 Testing Your Client
417
+
418
+ ```typescript
419
+ import { ExampleApiClient } from './my-client';
420
+
421
+ // Mock transport for testing
422
+ const mockTransport = {
423
+ connect: jest.fn().mockResolvedValue(undefined),
424
+ disconnect: jest.fn().mockResolvedValue(undefined),
425
+ request: jest.fn(),
426
+ on: jest.fn(),
427
+ off: jest.fn(),
428
+ getConnectionState: jest.fn().mockReturnValue({ status: 'connected' })
429
+ };
430
+
431
+ describe('ExampleApiClient', () => {
432
+ let client: ExampleApiClient;
433
+
434
+ beforeEach(() => {
435
+ client = new ExampleApiClient({
436
+ transport: 'websocket',
437
+ websocket: { url: 'ws://test' }
438
+ });
439
+
440
+ // Inject mock transport
441
+ (client as any).transport = mockTransport;
442
+ });
443
+
444
+ test('should get user profile', async () => {
445
+ const mockResponse = {
446
+ id: '123',
447
+ name: 'John Doe',
448
+ email: 'john@example.com',
449
+ createdAt: '2023-01-01T00:00:00Z'
450
+ };
451
+
452
+ mockTransport.request.mockResolvedValue({
453
+ payload: mockResponse
454
+ });
455
+
456
+ const result = await client.getUserProfile({ userId: '123' });
457
+
458
+ expect(result).toEqual(mockResponse);
459
+ expect(mockTransport.request).toHaveBeenCalledWith({
460
+ operation: 'getUserProfile',
461
+ payload: { userId: '123' }
462
+ });
463
+ });
464
+ });
465
+ ```
466
+
467
+ ## 🔄 Rust-AsyncAPI Compatibility
468
+
469
+ This template generates clients that are fully compatible with servers generated by the rust-asyncapi template:
470
+
471
+ ### Message Envelope Structure
472
+
473
+ Both clients and servers use the same message envelope format:
474
+
475
+ ```typescript
476
+ interface MessageEnvelope {
477
+ correlationId: string;
478
+ operation: string;
479
+ payload: any;
480
+ timestamp?: string;
481
+ error?: {
482
+ code: string;
483
+ message: string;
484
+ };
485
+ }
486
+ ```
487
+
488
+ ### Operation Naming
489
+
490
+ Operation names are derived consistently from the AsyncAPI specification:
491
+ - Channel addresses become method names (e.g., `user/profile` → `getUserProfile`)
492
+ - CamelCase conversion follows the same rules as rust-asyncapi
493
+ - Request/response patterns are automatically detected
494
+
495
+ ### Type Compatibility
496
+
497
+ - TypeScript interfaces match Rust struct definitions
498
+ - Optional fields are handled consistently
499
+ - Enum types are converted to TypeScript union types
500
+ - Date/time fields use ISO 8601 strings
501
+
502
+ ## 📚 API Reference
503
+
504
+ ### Client Configuration
505
+
506
+ ```typescript
507
+ interface ClientConfig {
508
+ transport: 'websocket' | 'http';
509
+ websocket?: WebSocketConfig;
510
+ http?: HttpConfig;
511
+ }
512
+
513
+ interface WebSocketConfig {
514
+ url: string;
515
+ reconnect?: boolean;
516
+ reconnectInterval?: number;
517
+ maxReconnectAttempts?: number;
518
+ timeout?: number;
519
+ auth?: AuthConfig;
520
+ }
521
+
522
+ interface HttpConfig {
523
+ baseUrl: string;
524
+ timeout?: number;
525
+ auth?: AuthConfig;
526
+ retry?: RetryConfig;
527
+ headers?: Record<string, string>;
528
+ }
529
+
530
+ interface AuthConfig {
531
+ token?: string;
532
+ apiKey?: string;
533
+ authorization?: string;
534
+ headers?: Record<string, string>;
535
+ }
536
+
537
+ interface RetryConfig {
538
+ attempts: number;
539
+ delay: number;
540
+ backoff?: 'linear' | 'exponential';
541
+ maxDelay?: number;
542
+ retryCondition?: (error: any) => boolean;
543
+ }
544
+ ```
545
+
546
+ ### Client Methods
547
+
548
+ ```typescript
549
+ class ExampleApiClient {
550
+ constructor(config: ClientConfig);
551
+
552
+ // Connection management
553
+ connect(): Promise<void>;
554
+ disconnect(): Promise<void>;
555
+ getConnectionState(): ConnectionState;
556
+
557
+ // Event handling
558
+ on(event: string, handler: Function): void;
559
+ off(event: string, handler: Function): void;
560
+
561
+ // Generated operation methods (based on your AsyncAPI spec)
562
+ getUserProfile(request: GetUserProfileRequest): Promise<UserProfile>;
563
+ createUser(request: CreateUserRequest): Promise<UserCreated>;
564
+ // ... other operations
565
+ }
566
+ ```
567
+
568
+ ## 🎯 Best Practices
569
+
570
+ 1. **Always handle errors**: Use try-catch blocks and specific error types
571
+ 2. **Clean up connections**: Always call `disconnect()` when done
572
+ 3. **Use TypeScript**: Take advantage of the generated types for better development experience
573
+ 4. **Configure retries**: Set appropriate retry policies for production use
574
+ 5. **Monitor connections**: Listen to connection events for better observability
575
+ 6. **Test thoroughly**: Use the provided testing patterns to ensure reliability
576
+
577
+ ## 🔗 Related Resources
578
+
579
+ - [AsyncAPI Specification](https://www.asyncapi.com/docs/reference/specification/v3.0.0)
580
+ - [AsyncAPI Generator](https://github.com/asyncapi/generator)
581
+ - [Rust AsyncAPI Template](https://github.com/asyncapi/rust-template)
582
+ - [TypeScript Documentation](https://www.typescriptlang.org/docs/)
583
+
584
+ ---
585
+
586
+ Generated with ❤️ by [AsyncAPI Generator](https://github.com/asyncapi/generator)