@app-studio/web 0.9.17 → 0.9.19

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.
Files changed (35) hide show
  1. package/dist/components/AuthGuard/index.d.ts +1 -1
  2. package/dist/utils/request.d.ts +2 -2
  3. package/dist/web.cjs.development.js +41 -46
  4. package/dist/web.cjs.development.js.map +1 -1
  5. package/dist/web.cjs.production.min.js +1 -1
  6. package/dist/web.cjs.production.min.js.map +1 -1
  7. package/dist/web.esm.js +43 -48
  8. package/dist/web.esm.js.map +1 -1
  9. package/dist/web.umd.development.js +45 -45
  10. package/dist/web.umd.development.js.map +1 -1
  11. package/dist/web.umd.production.min.js +1 -1
  12. package/dist/web.umd.production.min.js.map +1 -1
  13. package/docs/README.md +52 -0
  14. package/docs/adk-components.md +316 -0
  15. package/docs/adk-quick-start.md +294 -0
  16. package/docs/api-integration.md +801 -0
  17. package/docs/api-reference/README.md +103 -0
  18. package/docs/api-reference/data-display/flow.md +220 -0
  19. package/docs/api-reference/data-display/tree.md +210 -0
  20. package/docs/api-reference/form/chat-input.md +210 -0
  21. package/docs/api-reference/utility/button.md +145 -0
  22. package/docs/api-reference/utility/title.md +301 -0
  23. package/docs/app-studio.md +302 -0
  24. package/docs/component-development/guide.md +546 -0
  25. package/docs/contributing/documentation.md +153 -0
  26. package/docs/conventions.md +536 -0
  27. package/docs/design-system/theming.md +299 -0
  28. package/docs/documentation-system.md +143 -0
  29. package/docs/getting-started/component-usage.md +211 -0
  30. package/docs/getting-started/introduction.md +114 -0
  31. package/docs/guide.md +550 -0
  32. package/docs/integration-guide.md +449 -0
  33. package/docs/tutorials/README.md +51 -0
  34. package/docs/tutorials/basic/creating-a-simple-form.md +566 -0
  35. package/package.json +3 -2
@@ -0,0 +1,801 @@
1
+ # API Integration Guide
2
+
3
+ This guide explains how to integrate with backend APIs in the Front-Starter project. The project uses OpenAPI-generated services for type-safe API calls.
4
+
5
+ ## API Service Structure
6
+
7
+ API services are generated from OpenAPI/Swagger specifications and stored in the `src/services/api` directory. These services provide type-safe methods for interacting with the backend API.
8
+
9
+ Each service in the `src/services/api` directory contains:
10
+
11
+ 1. **Base API functions** - Direct functions that return CancelablePromise objects
12
+ 2. **Generated hooks** - React hooks that wrap the base functions with the `useRequest` hook from `@app-studio/react-request`
13
+
14
+ For example, a typical service like `UserService` contains functions like:
15
+
16
+ ```typescript
17
+ // Base API function
18
+ export const userControllerRead = (id: string): CancelablePromise<any> => {
19
+ return __request({
20
+ method: 'GET',
21
+ path: `/users/${id}`,
22
+ errors: {
23
+ 404: `User not found`,
24
+ },
25
+ });
26
+ };
27
+
28
+ // Generated hook that wraps the base function
29
+ export const useUserControllerReadService = ({ method = 'GET', ...options }: UseRequestOption = {}): {
30
+ run: (id: string) => void;
31
+ data: any;
32
+ } & UseRequestProperties => {
33
+ return useRequest(userControllerRead, { method, ...options });
34
+ };
35
+ ```
36
+
37
+ These generated hooks provide a consistent interface for making API requests with built-in loading, error, and success states.
38
+
39
+ ## Generating API Services
40
+
41
+ The project includes scripts to generate API services from OpenAPI/Swagger specifications:
42
+
43
+ ```bash
44
+ # Generate API services from local Swagger docs
45
+ npm run api:local
46
+ # or
47
+ yarn api:local
48
+
49
+ # Generate API services from remote Swagger docs
50
+ npm run api
51
+ # or
52
+ yarn api
53
+ ```
54
+
55
+ These scripts generate TypeScript files in the `src/services/api` directory based on the API specification.
56
+
57
+ ### How API Services Are Generated
58
+
59
+ The API services are automatically generated using the `react-api` tool with the following command:
60
+
61
+ ```bash
62
+ react-api --useUnionTypes --input http://localhost:3000/docs/swagger.json --output ./src/services/api && prettier --write ./src/services/api
63
+ ```
64
+
65
+ This command:
66
+ 1. Uses the `react-api` tool from the `@app-studio/react-api` package
67
+ 2. Enables union types with the `--useUnionTypes` flag for better TypeScript type safety
68
+ 3. Reads the OpenAPI/Swagger specification from `http://localhost:3000/docs/swagger.json`
69
+ 4. Outputs the generated TypeScript files to the `./src/services/api` directory
70
+ 5. Formats the generated code with Prettier
71
+
72
+ The generated services include both the base API functions and React hooks that wrap these functions with the `useRequest` hook from `@app-studio/react-request`.
73
+
74
+ ## API Configuration
75
+
76
+ The API base URL is configured in `src/configs/AppConfig.ts`:
77
+
78
+ ```typescript
79
+ export const API_URL = env.REACT_APP_API_URL ? env.REACT_APP_API_URL : 'http://localhost:3000';
80
+ ```
81
+
82
+ The OpenAPI configuration is set up in `src/utils/request.ts`:
83
+
84
+ ```typescript
85
+ import { OpenAPI } from 'src/services/api';
86
+
87
+ // Set the base URL for API requests
88
+ OpenAPI.BASE = API_URL;
89
+ OpenAPI.CORS = 'cors';
90
+ ```
91
+
92
+ ## Authentication
93
+
94
+ Authentication tokens are managed in the `AuthStore` and set in the OpenAPI configuration:
95
+
96
+ ```typescript
97
+ // Set the authentication token for API requests
98
+ export async function setToken(token: string) {
99
+ access_token = token;
100
+ OpenAPI.TOKEN = token;
101
+ }
102
+ ```
103
+
104
+ ### Using Auth Hooks
105
+
106
+ The project provides several authentication-related hooks in the API services:
107
+
108
+ ```typescript
109
+ // Login example
110
+ import { AuthService } from 'src/services/api';
111
+ import { useAuthStore } from 'src/stores/AuthStore';
112
+
113
+ const LoginForm = () => {
114
+ const { setUser, setToken } = useAuthStore();
115
+
116
+ const loginRequest = AuthService.useAuthControllerLoginService({
117
+ onSuccess: (data) => {
118
+ // Store the token and user data
119
+ setToken(data.token);
120
+ setUser(data.user);
121
+
122
+ // Redirect to dashboard
123
+ navigate('/dashboard');
124
+ },
125
+ onError: (error) => {
126
+ showToast('error', 'Login Failed', error.message);
127
+ },
128
+ });
129
+
130
+ const handleSubmit = (values) => {
131
+ loginRequest.run({
132
+ email: values.email,
133
+ password: values.password,
134
+ });
135
+ };
136
+
137
+ return (
138
+ <form onSubmit={handleSubmit}>
139
+ {/* Form fields */}
140
+ </form>
141
+ );
142
+ };
143
+ ```
144
+
145
+ ### Handling Expired Tokens
146
+
147
+ To handle expired tokens, you can set up a global error handler in the request configuration:
148
+
149
+ ```typescript
150
+ // In src/utils/request.ts
151
+ import { OpenAPI, ApiError } from 'src/services/api';
152
+ import { useAuthStore } from 'src/stores/AuthStore';
153
+
154
+ // Set up global error handler
155
+ const originalFetch = window.fetch;
156
+ window.fetch = async (...args) => {
157
+ try {
158
+ const response = await originalFetch(...args);
159
+
160
+ // Handle 401 Unauthorized errors
161
+ if (response.status === 401) {
162
+ const { logout } = useAuthStore.getState();
163
+ logout();
164
+ window.location.href = '/login?expired=true';
165
+ }
166
+
167
+ return response;
168
+ } catch (error) {
169
+ console.error('API request failed:', error);
170
+ throw error;
171
+ }
172
+ };
173
+ ```
174
+
175
+ ## Making API Requests
176
+
177
+ ### Request Hooks
178
+
179
+ The recommended way to make API requests is to create request hooks in `.request.ts` files. These hooks wrap the API services and provide loading, error, and success states.
180
+
181
+ #### Using Generated Hooks
182
+
183
+ The simplest approach is to use the generated hooks directly from the API services:
184
+
185
+ ```typescript
186
+ // example.component.tsx
187
+ import { UserService } from 'src/services/api';
188
+
189
+ export const UserProfile = ({ userId }) => {
190
+ const getUserRequest = UserService.useUserControllerReadService({
191
+ onSuccess: (data) => {
192
+ console.log('User data fetched:', data);
193
+ },
194
+ onError: (error) => {
195
+ console.error('Error fetching user:', error);
196
+ },
197
+ });
198
+
199
+ useEffect(() => {
200
+ getUserRequest.run(userId);
201
+ }, [userId]);
202
+
203
+ return (
204
+ <div>
205
+ {getUserRequest.loading ? (
206
+ <p>Loading...</p>
207
+ ) : getUserRequest.data ? (
208
+ <div>
209
+ <h2>{getUserRequest.data.name}</h2>
210
+ <p>{getUserRequest.data.email}</p>
211
+ </div>
212
+ ) : null}
213
+ </div>
214
+ );
215
+ };
216
+ ```
217
+
218
+ #### Creating Custom Request Hooks
219
+
220
+ For more complex scenarios, create custom request hooks in `.request.ts` files that combine multiple API services:
221
+
222
+ ```typescript
223
+ // example.request.ts
224
+ import { useRequest } from '@app-studio/react-request';
225
+ import { ExampleService } from 'src/services/api';
226
+
227
+ export const useExampleRequests = (callbacks = {}) => {
228
+ const fetchDataRequest = useRequest({
229
+ request: ExampleService.exampleControllerFindAll,
230
+ onSuccess: callbacks.onFetchDataSuccess,
231
+ onError: callbacks.onFetchDataError,
232
+ onFetch: (params) => {
233
+ // This runs when the request is made
234
+ console.log('Fetching data with params:', params);
235
+ },
236
+ });
237
+
238
+ const createItemRequest = useRequest({
239
+ request: ExampleService.exampleControllerCreate,
240
+ onSuccess: (data) => {
241
+ // You can refresh the data list after creating a new item
242
+ fetchDataRequest.run();
243
+
244
+ // And call the provided callback
245
+ if (callbacks.onCreateItemSuccess) {
246
+ callbacks.onCreateItemSuccess(data);
247
+ }
248
+ },
249
+ onError: callbacks.onCreateItemError,
250
+ });
251
+
252
+ return {
253
+ fetchDataRequest,
254
+ createItemRequest,
255
+ };
256
+ };
257
+ ```
258
+
259
+ #### useRequest Options
260
+
261
+ The `useRequest` hook and the generated service hooks accept several options:
262
+
263
+ - `method`: HTTP method to use (GET, POST, etc.)
264
+ - `onSuccess`: Callback function that runs when the request succeeds
265
+ - `onError`: Callback function that runs when the request fails
266
+ - `onFetch`: Callback function that runs when the request is made
267
+ - `initialData`: Initial data to use before the request completes
268
+ - `manual`: Whether to run the request manually (default: true)
269
+ - `debounce`: Debounce time in milliseconds
270
+ - `throttle`: Throttle time in milliseconds
271
+
272
+ ### Using Request Hooks in Components
273
+
274
+ Request hooks can be used in components to make API calls and handle responses:
275
+
276
+ ```typescript
277
+ // example.component.tsx
278
+ import React, { useEffect } from 'react';
279
+ import { useExampleRequests } from './example.request';
280
+
281
+ export const ExampleComponent = () => {
282
+ const { fetchDataRequest, createItemRequest } = useExampleRequests({
283
+ onFetchDataSuccess: (data) => {
284
+ console.log('Data fetched successfully:', data);
285
+ },
286
+ onFetchDataError: (error) => {
287
+ console.error('Error fetching data:', error);
288
+ },
289
+ });
290
+
291
+ useEffect(() => {
292
+ // Fetch data when the component mounts
293
+ fetchDataRequest.run();
294
+ }, []);
295
+
296
+ const handleCreateItem = () => {
297
+ createItemRequest.run({
298
+ name: 'New Item',
299
+ description: 'This is a new item',
300
+ });
301
+ };
302
+
303
+ return (
304
+ <div>
305
+ {fetchDataRequest.loading ? (
306
+ <p>Loading...</p>
307
+ ) : fetchDataRequest.error ? (
308
+ <p>Error: {fetchDataRequest.error.message}</p>
309
+ ) : (
310
+ <ul>
311
+ {fetchDataRequest.data?.map((item) => (
312
+ <li key={item.id}>{item.name}</li>
313
+ ))}
314
+ </ul>
315
+ )}
316
+
317
+ <button onClick={handleCreateItem} disabled={createItemRequest.loading}>
318
+ Create Item
319
+ </button>
320
+ </div>
321
+ );
322
+ };
323
+ ```
324
+
325
+ ## Error Handling
326
+
327
+ Error handling is an important part of API integration. The request hooks provide error states that can be used to display error messages to the user.
328
+
329
+ ### Basic Error Handling
330
+
331
+ ```typescript
332
+ // Error handling in a component
333
+ if (fetchDataRequest.error) {
334
+ return (
335
+ <div>
336
+ <p>Error: {fetchDataRequest.error.message}</p>
337
+ <button onClick={() => fetchDataRequest.run()}>Retry</button>
338
+ </div>
339
+ );
340
+ }
341
+ ```
342
+
343
+ ### Using showToast for User Feedback
344
+
345
+ You can use the `showToast` function from `@app-studio/web` to display error messages:
346
+
347
+ ```typescript
348
+ import { showToast } from '@app-studio/web';
349
+
350
+ // In the onError callback
351
+ onError: (error) => {
352
+ showToast('error', 'Error', error.message || 'An error occurred');
353
+ }
354
+ ```
355
+
356
+ ### Centralized Error Handling
357
+
358
+ For consistent error handling across your application, create a centralized error handler:
359
+
360
+ ```typescript
361
+ // utils/errorHandler.ts
362
+ import { showToast } from '@app-studio/web';
363
+ import { ApiError } from 'src/services/api';
364
+
365
+ export const handleApiError = (error: any, title = 'Error') => {
366
+ if (error instanceof ApiError) {
367
+ // Handle specific API error codes
368
+ switch (error.status) {
369
+ case 400:
370
+ showToast('error', title, 'Invalid request. Please check your input.');
371
+ break;
372
+ case 401:
373
+ showToast('error', title, 'Authentication required. Please log in again.');
374
+ // Redirect to login page
375
+ window.location.href = '/login';
376
+ break;
377
+ case 403:
378
+ showToast('error', title, 'You do not have permission to perform this action.');
379
+ break;
380
+ case 404:
381
+ showToast('error', title, 'The requested resource was not found.');
382
+ break;
383
+ case 500:
384
+ showToast('error', title, 'Server error. Please try again later.');
385
+ break;
386
+ default:
387
+ showToast('error', title, error.message || 'An unexpected error occurred.');
388
+ }
389
+ } else {
390
+ // Handle non-API errors
391
+ showToast('error', title, error.message || 'An unexpected error occurred.');
392
+ }
393
+
394
+ // Log the error for debugging
395
+ console.error('API Error:', error);
396
+ };
397
+
398
+ // Using the centralized error handler in your request hooks
399
+ const fetchDataRequest = useRequest({
400
+ request: ExampleService.exampleControllerFindAll,
401
+ onSuccess: (data) => {
402
+ // Handle success
403
+ },
404
+ onError: (error) => {
405
+ handleApiError(error, 'Data Fetch Failed');
406
+ },
407
+ });
408
+ ```
409
+
410
+ ### Error Boundaries for React Components
411
+
412
+ Use React Error Boundaries to catch and handle errors in your components:
413
+
414
+ ```typescript
415
+ // components/ErrorBoundary.tsx
416
+ import React, { Component, ErrorInfo, ReactNode } from 'react';
417
+
418
+ interface Props {
419
+ children: ReactNode;
420
+ fallback?: ReactNode;
421
+ }
422
+
423
+ interface State {
424
+ hasError: boolean;
425
+ error?: Error;
426
+ }
427
+
428
+ class ErrorBoundary extends Component<Props, State> {
429
+ constructor(props: Props) {
430
+ super(props);
431
+ this.state = { hasError: false };
432
+ }
433
+
434
+ static getDerivedStateFromError(error: Error): State {
435
+ return { hasError: true, error };
436
+ }
437
+
438
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
439
+ console.error('Component error:', error, errorInfo);
440
+ }
441
+
442
+ render(): ReactNode {
443
+ if (this.state.hasError) {
444
+ return this.props.fallback || (
445
+ <div>
446
+ <h2>Something went wrong.</h2>
447
+ <p>{this.state.error?.message}</p>
448
+ <button onClick={() => this.setState({ hasError: false })}>Try again</button>
449
+ </div>
450
+ );
451
+ }
452
+
453
+ return this.props.children;
454
+ }
455
+ }
456
+
457
+ // Using the error boundary in your components
458
+ const DataComponent = () => (
459
+ <ErrorBoundary>
460
+ <UserList />
461
+ </ErrorBoundary>
462
+ );
463
+ ```
464
+
465
+ ## Pagination and Filtering
466
+
467
+ For paginated API endpoints, you can pass pagination parameters to the request:
468
+
469
+ ```typescript
470
+ // Fetch paginated data
471
+ fetchDataRequest.run({
472
+ take: 10,
473
+ skip: (currentPage - 1) * 10,
474
+ filter: searchTerm,
475
+ sortBy: 'createdAt',
476
+ sortOrder: 'desc',
477
+ });
478
+ ```
479
+
480
+ ## Caching and Data Persistence
481
+
482
+ The request hooks do not include built-in caching. If you need to cache API responses, you can store the data in a Zustand store:
483
+
484
+ ```typescript
485
+ // stores/DataStore.ts
486
+ import { create } from 'zustand';
487
+
488
+ interface DataState {
489
+ items: any[];
490
+ setItems: (items: any[]) => void;
491
+ isLoading: boolean;
492
+ setLoading: (isLoading: boolean) => void;
493
+ error: Error | null;
494
+ setError: (error: Error | null) => void;
495
+ fetchItems: () => Promise<void>;
496
+ }
497
+
498
+ export const useDataStore = create<DataState>((set, get) => ({
499
+ items: [],
500
+ setItems: (items) => set({ items }),
501
+ isLoading: false,
502
+ setLoading: (isLoading) => set({ isLoading }),
503
+ error: null,
504
+ setError: (error) => set({ error }),
505
+
506
+ // Fetch items from the API
507
+ fetchItems: async () => {
508
+ try {
509
+ set({ isLoading: true, error: null });
510
+
511
+ // Import the API service directly in the action
512
+ const { ExampleService } = await import('src/services/api');
513
+
514
+ // Call the API directly (not using hooks in stores)
515
+ const data = await ExampleService.exampleControllerFindAll();
516
+
517
+ set({ items: data, isLoading: false });
518
+ return data;
519
+ } catch (error) {
520
+ set({ error: error as Error, isLoading: false });
521
+ throw error;
522
+ }
523
+ },
524
+ }));
525
+
526
+ // In your component
527
+ import { useDataStore } from 'src/stores/DataStore';
528
+ import { useEffect } from 'react';
529
+
530
+ const ExampleComponent = () => {
531
+ const { items, isLoading, error, fetchItems } = useDataStore();
532
+
533
+ useEffect(() => {
534
+ fetchItems();
535
+ }, []);
536
+
537
+ return (
538
+ <div>
539
+ {isLoading ? (
540
+ <p>Loading...</p>
541
+ ) : error ? (
542
+ <p>Error: {error.message}</p>
543
+ ) : (
544
+ <ul>
545
+ {items.map((item) => (
546
+ <li key={item.id}>{item.name}</li>
547
+ ))}
548
+ </ul>
549
+ )}
550
+ </div>
551
+ );
552
+ };
553
+ ```
554
+
555
+ ### Integrating API Hooks with Zustand Stores
556
+
557
+ For more complex state management, you can combine API hooks with Zustand stores:
558
+
559
+ ```typescript
560
+ // Component using both API hooks and Zustand store
561
+ import { useEffect } from 'react';
562
+ import { UserService } from 'src/services/api';
563
+ import { useDataStore } from 'src/stores/DataStore';
564
+
565
+ const UserList = () => {
566
+ // API hook for fetching users
567
+ const getUsersRequest = UserService.useUserControllerFindService({
568
+ onSuccess: (data) => {
569
+ // Update the store with the fetched data
570
+ setItems(data);
571
+ },
572
+ });
573
+
574
+ // Zustand store for caching the data
575
+ const { items, setItems } = useDataStore();
576
+
577
+ useEffect(() => {
578
+ // If we already have cached data, don't fetch again
579
+ if (items.length === 0) {
580
+ getUsersRequest.run();
581
+ }
582
+ }, []);
583
+
584
+ return (
585
+ <div>
586
+ {getUsersRequest.loading ? (
587
+ <p>Loading...</p>
588
+ ) : getUsersRequest.error ? (
589
+ <p>Error: {getUsersRequest.error.message}</p>
590
+ ) : (
591
+ <ul>
592
+ {items.map((user) => (
593
+ <li key={user.id}>{user.name}</li>
594
+ ))}
595
+ </ul>
596
+ )}
597
+ </div>
598
+ );
599
+ };
600
+ ```
601
+
602
+ ## File Uploads
603
+
604
+ For file uploads, you can use the `MediaUploader` component:
605
+
606
+ ```typescript
607
+ import { MediaUploader } from 'src/components/MediaUploader';
608
+
609
+ <MediaUploader
610
+ onUpload={(file) => {
611
+ uploadFileRequest.run({
612
+ file,
613
+ type: 'image',
614
+ });
615
+ }}
616
+ accept="image/*"
617
+ />
618
+ ```
619
+
620
+ ## Websockets and Real-time Data
621
+
622
+ For real-time data, you can use the `multimodalLiveClient` utility:
623
+
624
+ ```typescript
625
+ import { LiveClientSetup } from 'src/lib/multimodalLiveClient';
626
+
627
+ // Set up a real-time connection
628
+ const client = new LiveClientSetup({
629
+ url: 'wss://api.example.com/ws',
630
+ token: authToken,
631
+ });
632
+
633
+ // Listen for messages
634
+ client.on('message', (message) => {
635
+ console.log('Received message:', message);
636
+ });
637
+
638
+ // Send a message
639
+ client.send({
640
+ type: 'chat',
641
+ content: 'Hello, world!',
642
+ });
643
+ ```
644
+
645
+ ## Advanced API Integration Patterns
646
+
647
+ ### Request Orchestration
648
+
649
+ For complex pages that require multiple API calls, it's recommended to create a centralized request module like `editor.request.ts`. This approach provides several benefits:
650
+
651
+ 1. **Centralized API logic**: All API calls are defined in one place
652
+ 2. **Coordinated data fetching**: Easily chain and coordinate multiple API calls
653
+ 3. **Reusable request hooks**: Create custom hooks that can be used across components
654
+ 4. **Consistent error handling**: Implement consistent error handling for all API calls
655
+
656
+ Here's an example based on the pattern used in `src/pages/editor/editor.request.ts`:
657
+
658
+ ```typescript
659
+ // page.request.ts
660
+ import {
661
+ UserService,
662
+ ContentService,
663
+ CommentService
664
+ } from 'src/services/api';
665
+
666
+ const usePageRequests = ({
667
+ // Callbacks for different operations
668
+ getUserCallback = (data) => {},
669
+ getContentCallback = (data) => {},
670
+ getCommentsCallback = (data) => {},
671
+ onError = () => {},
672
+ }) => {
673
+ // Get user data
674
+ const getUserRequest = UserService.useUserControllerReadService({
675
+ onSuccess: (data) => {
676
+ getUserCallback(data);
677
+
678
+ // After getting user data, fetch their content
679
+ if (data?.id) {
680
+ getContentRequest.run({ userId: data.id });
681
+ }
682
+ },
683
+ onError,
684
+ });
685
+
686
+ // Get content data
687
+ const getContentRequest = ContentService.useContentControllerFindService({
688
+ onSuccess: (data) => {
689
+ getContentCallback(data);
690
+
691
+ // After getting content, fetch comments for each content item
692
+ if (data?.items?.length) {
693
+ data.items.forEach(item => {
694
+ getCommentsRequest.run({ contentId: item.id });
695
+ });
696
+ }
697
+ },
698
+ onError,
699
+ });
700
+
701
+ // Get comments
702
+ const getCommentsRequest = CommentService.useCommentControllerFindService({
703
+ onSuccess: getCommentsCallback,
704
+ onError,
705
+ });
706
+
707
+ // Create comment
708
+ const createCommentRequest = CommentService.useCommentControllerCreateService({
709
+ onSuccess: (data) => {
710
+ // Refresh comments after creating a new one
711
+ getCommentsRequest.run({ contentId: data.contentId });
712
+ },
713
+ onError,
714
+ });
715
+
716
+ return {
717
+ getUserRequest,
718
+ getContentRequest,
719
+ getCommentsRequest,
720
+ createCommentRequest,
721
+ };
722
+ };
723
+
724
+ export default usePageRequests;
725
+ ```
726
+
727
+ ### Composing Multiple API Requests
728
+
729
+ When working with complex data flows, you often need to chain API requests together. Here are some common patterns:
730
+
731
+ #### Sequential Requests
732
+
733
+ Use the `onSuccess` callback to trigger dependent requests:
734
+
735
+ ```typescript
736
+ const getPageRequest = PageService.usePageControllerReadService({
737
+ onSuccess: (data) => {
738
+ // Store the page data
739
+ getPageCallback(data);
740
+
741
+ // If the page has a workflow, fetch it
742
+ if (data?.workflowId) {
743
+ getWorkflowRequest.run(data.workflowId);
744
+ }
745
+ },
746
+ });
747
+ ```
748
+
749
+ #### Refreshing Data After Mutations
750
+
751
+ After creating, updating, or deleting data, refresh the relevant data:
752
+
753
+ ```typescript
754
+ const deleteComponentRequest = ComponentService.useComponentControllerDeleteService({
755
+ onSuccess: () => {
756
+ // Refresh the page data after deleting a component
757
+ getPageRequest.run(id);
758
+ },
759
+ });
760
+ ```
761
+
762
+ #### Handling Request Dependencies
763
+
764
+ When one request depends on the result of another:
765
+
766
+ ```typescript
767
+ // First, get the user
768
+ getUserRequest.run(userId);
769
+
770
+ // In the onSuccess callback of getUserRequest
771
+ onSuccess: (userData) => {
772
+ // Then get the user's content
773
+ getContentRequest.run({ userId: userData.id });
774
+
775
+ // In the onSuccess callback of getContentRequest
776
+ onSuccess: (contentData) => {
777
+ // Finally, get comments for each content item
778
+ contentData.items.forEach(item => {
779
+ getCommentsRequest.run({ contentId: item.id });
780
+ });
781
+ }
782
+ }
783
+ ```
784
+
785
+ ## Best Practices
786
+
787
+ 1. **Use Generated API Hooks**: Leverage the hooks from `@src/services/api/` for type-safe API calls
788
+ 2. **Create Request Modules**: For complex pages, create dedicated `.request.ts` files that orchestrate multiple API calls
789
+ 3. **Chain Requests Properly**: Use `onSuccess` callbacks to chain dependent requests
790
+ 4. **Handle Loading States**: Always show loading indicators when requests are in progress
791
+ 5. **Implement Error Handling**: Use `onError` callbacks to handle errors consistently
792
+ 6. **Refresh Data After Mutations**: Update relevant data after create, update, or delete operations
793
+ 7. **Use Callbacks for Component Communication**: Pass callback functions to request hooks to communicate with components
794
+ 8. **Type Safety**: Use the generated TypeScript types for request parameters and responses
795
+ 9. **Pagination**: Implement pagination for large data sets
796
+ 10. **Caching**: Consider using Zustand stores to cache frequently accessed data
797
+ 11. **Authentication**: Ensure authentication tokens are properly managed in the AuthStore
798
+ 12. **Validation**: Validate user input before sending it to the API
799
+ 13. **Testing**: Write tests for API integration to ensure it works as expected
800
+
801
+ By following these guidelines and leveraging the patterns from the codebase, you can create robust and maintainable API integration in your Front-Starter project.