@qlover/create-app 0.6.2 → 0.7.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/CHANGELOG.md +53 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/react-app/README.en.md +257 -0
- package/dist/templates/react-app/README.md +29 -231
- package/dist/templates/react-app/__tests__/__mocks__/I18nService.ts +13 -0
- package/dist/templates/react-app/__tests__/__mocks__/MockAppConfit.ts +48 -0
- package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +16 -0
- package/dist/templates/react-app/__tests__/__mocks__/MockLogger.ts +14 -0
- package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +92 -0
- package/dist/templates/react-app/__tests__/setup/index.ts +51 -0
- package/dist/templates/react-app/__tests__/src/App.test.tsx +139 -0
- package/dist/templates/react-app/__tests__/src/base/cases/AppConfig.test.ts +288 -0
- package/dist/templates/react-app/__tests__/src/base/cases/AppError.test.ts +102 -0
- package/dist/templates/react-app/__tests__/src/base/cases/DialogHandler.test.ts +228 -0
- package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +207 -0
- package/dist/templates/react-app/__tests__/src/base/cases/InversifyContainer.test.ts +181 -0
- package/dist/templates/react-app/__tests__/src/base/cases/PublicAssetsPath.test.ts +61 -0
- package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +199 -0
- package/dist/templates/react-app/__tests__/src/base/cases/RequestStatusCatcher.test.ts +192 -0
- package/dist/templates/react-app/__tests__/src/base/cases/RouterLoader.test.ts +235 -0
- package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +224 -0
- package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +257 -0
- package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +72 -0
- package/dist/templates/react-app/__tests__/src/main.integration.test.tsx +62 -0
- package/dist/templates/react-app/__tests__/src/main.test.tsx +46 -0
- package/dist/templates/react-app/__tests__/src/uikit/components/BaseHeader.test.tsx +88 -0
- package/dist/templates/react-app/config/app.router.ts +155 -0
- package/dist/templates/react-app/config/common.ts +9 -1
- package/dist/templates/react-app/docs/en/bootstrap.md +562 -0
- package/dist/templates/react-app/docs/en/development-guide.md +523 -0
- package/dist/templates/react-app/docs/en/env.md +482 -0
- package/dist/templates/react-app/docs/en/global.md +509 -0
- package/dist/templates/react-app/docs/en/i18n.md +268 -0
- package/dist/templates/react-app/docs/en/index.md +173 -0
- package/dist/templates/react-app/docs/en/ioc.md +424 -0
- package/dist/templates/react-app/docs/en/project-structure.md +434 -0
- package/dist/templates/react-app/docs/en/request.md +425 -0
- package/dist/templates/react-app/docs/en/router.md +404 -0
- package/dist/templates/react-app/docs/en/store.md +321 -0
- package/dist/templates/react-app/docs/en/test-guide.md +782 -0
- package/dist/templates/react-app/docs/en/theme.md +424 -0
- package/dist/templates/react-app/docs/en/typescript-guide.md +473 -0
- package/dist/templates/react-app/docs/zh/bootstrap.md +7 -0
- package/dist/templates/react-app/docs/zh/development-guide.md +523 -0
- package/dist/templates/react-app/docs/zh/env.md +24 -25
- package/dist/templates/react-app/docs/zh/global.md +28 -27
- package/dist/templates/react-app/docs/zh/i18n.md +268 -0
- package/dist/templates/react-app/docs/zh/index.md +173 -0
- package/dist/templates/react-app/docs/zh/ioc.md +44 -32
- package/dist/templates/react-app/docs/zh/project-structure.md +434 -0
- package/dist/templates/react-app/docs/zh/request.md +429 -0
- package/dist/templates/react-app/docs/zh/router.md +408 -0
- package/dist/templates/react-app/docs/zh/store.md +321 -0
- package/dist/templates/react-app/docs/zh/test-guide.md +782 -0
- package/dist/templates/react-app/docs/zh/theme.md +424 -0
- package/dist/templates/react-app/docs/zh/typescript-guide.md +473 -0
- package/dist/templates/react-app/package.json +9 -20
- package/dist/templates/react-app/src/base/cases/AppConfig.ts +16 -9
- package/dist/templates/react-app/src/base/cases/PublicAssetsPath.ts +7 -1
- package/dist/templates/react-app/src/base/services/I18nService.ts +15 -4
- package/dist/templates/react-app/src/base/services/RouteService.ts +43 -7
- package/dist/templates/react-app/src/core/bootstraps/BootstrapApp.ts +31 -10
- package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +1 -1
- package/dist/templates/react-app/src/core/globals.ts +1 -3
- package/dist/templates/react-app/src/core/registers/RegisterCommon.ts +5 -3
- package/dist/templates/react-app/src/main.tsx +6 -1
- package/dist/templates/react-app/src/pages/404.tsx +0 -1
- package/dist/templates/react-app/src/pages/500.tsx +1 -1
- package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +3 -1
- package/dist/templates/react-app/src/styles/css/antd-themes/dark.css +3 -1
- package/dist/templates/react-app/src/styles/css/antd-themes/index.css +1 -1
- package/dist/templates/react-app/src/styles/css/antd-themes/pink.css +6 -1
- package/dist/templates/react-app/src/styles/css/page.css +1 -1
- package/dist/templates/react-app/src/uikit/components/BaseHeader.tsx +9 -2
- package/dist/templates/react-app/src/uikit/components/LocaleLink.tsx +5 -3
- package/dist/templates/react-app/src/uikit/hooks/useI18nGuard.ts +4 -6
- package/dist/templates/react-app/tsconfig.json +2 -1
- package/dist/templates/react-app/tsconfig.test.json +13 -0
- package/dist/templates/react-app/vite.config.ts +3 -2
- package/package.json +1 -1
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
# Request System
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The request system is implemented based on the request module of `@qlover/fe-corekit`, providing a powerful and extensible HTTP request solution. The system adopts a plugin architecture, supporting request interception, response handling, error handling, and other features.
|
|
6
|
+
|
|
7
|
+
## Core Features
|
|
8
|
+
|
|
9
|
+
- 🔌 **Plugin Architecture**: Supports custom plugin extensions
|
|
10
|
+
- 🔄 **Request Scheduling**: Unified request scheduling management
|
|
11
|
+
- 🛡️ **Type Safety**: Complete TypeScript support
|
|
12
|
+
- 🎯 **Request Cancellation**: Supports request cancellation
|
|
13
|
+
- 📝 **Logging**: Detailed request logging
|
|
14
|
+
- 🔧 **Mock Support**: Built-in data mocking functionality
|
|
15
|
+
|
|
16
|
+
## Basic Architecture
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// Request system architecture
|
|
20
|
+
Request → Scheduler → Adapter → Plugins → Network
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### 1. Creating API Service
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { RequestAdapterFetch, RequestScheduler } from '@qlover/fe-corekit';
|
|
29
|
+
|
|
30
|
+
@injectable()
|
|
31
|
+
export class FeApi extends RequestScheduler<FeApiConfig> {
|
|
32
|
+
constructor(@inject(FeApiAdapter) adapter: RequestAdapterFetch) {
|
|
33
|
+
super(adapter);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// API method definition
|
|
37
|
+
async getIpInfo(): Promise<FeApiGetIpInfo['response']> {
|
|
38
|
+
return this.get('http://ip-api.com/json/');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Configuring Request Adapter
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { RequestAdapterFetch } from '@qlover/fe-corekit';
|
|
47
|
+
|
|
48
|
+
const apiAdapter = new RequestAdapterFetch({
|
|
49
|
+
responseType: 'json',
|
|
50
|
+
baseURL: '/api'
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Configure plugins
|
|
54
|
+
apiAdapter.usePlugin(new FetchURLPlugin());
|
|
55
|
+
apiAdapter.usePlugin(
|
|
56
|
+
new RequestCommonPlugin({
|
|
57
|
+
tokenPrefix: 'Bearer',
|
|
58
|
+
token: () => getToken()
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3. Registering Request Service
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// core/registers/RegisterCommon.ts
|
|
67
|
+
export const RegisterCommon: IOCRegister = {
|
|
68
|
+
register(container) {
|
|
69
|
+
// Register request plugins
|
|
70
|
+
const feApiAbort = new FetchAbortPlugin();
|
|
71
|
+
const feApiRequestCommonPlugin = new RequestCommonPlugin({
|
|
72
|
+
tokenPrefix: AppConfig.openAiTokenPrefix,
|
|
73
|
+
requiredToken: true,
|
|
74
|
+
token: () => container.get(UserService).getToken()
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
container.bind(FetchAbortPlugin, feApiAbort);
|
|
78
|
+
container.bind(IOCIdentifier.FeApiCommonPlugin, feApiRequestCommonPlugin);
|
|
79
|
+
container.bind(
|
|
80
|
+
IOCIdentifier.ApiMockPlugin,
|
|
81
|
+
new ApiMockPlugin(mockDataJson, container.get(Logger))
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 4. Using in Components
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
@injectable()
|
|
91
|
+
export class RequestController extends StoreInterface<RequestControllerState> {
|
|
92
|
+
constructor(
|
|
93
|
+
@inject(FeApi) private readonly feApi: FeApi,
|
|
94
|
+
@inject(UserApi) private readonly userApi: UserApi
|
|
95
|
+
) {
|
|
96
|
+
super(createDefaultState);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async fetchData() {
|
|
100
|
+
if (this.state.loading) return;
|
|
101
|
+
|
|
102
|
+
this.setState({ loading: true });
|
|
103
|
+
try {
|
|
104
|
+
const result = await this.feApi.getData();
|
|
105
|
+
this.setState({ loading: false, data: result });
|
|
106
|
+
} catch (error) {
|
|
107
|
+
this.setState({ loading: false, error });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Plugin System
|
|
114
|
+
|
|
115
|
+
### 1. Built-in Plugins
|
|
116
|
+
|
|
117
|
+
#### FetchURLPlugin
|
|
118
|
+
|
|
119
|
+
- URL processing and construction
|
|
120
|
+
- Parameter serialization
|
|
121
|
+
- baseURL handling
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
apiAdapter.usePlugin(new FetchURLPlugin());
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### RequestCommonPlugin
|
|
128
|
+
|
|
129
|
+
- Common header settings
|
|
130
|
+
- Token management
|
|
131
|
+
- Authentication handling
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
apiAdapter.usePlugin(
|
|
135
|
+
new RequestCommonPlugin({
|
|
136
|
+
tokenPrefix: 'Bearer',
|
|
137
|
+
requiredToken: true,
|
|
138
|
+
token: () => getToken()
|
|
139
|
+
})
|
|
140
|
+
);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### ApiMockPlugin
|
|
144
|
+
|
|
145
|
+
- Mock data support
|
|
146
|
+
- Development environment debugging
|
|
147
|
+
- Interface simulation
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
apiAdapter.usePlugin(new ApiMockPlugin(mockDataJson, logger));
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### RequestLogger
|
|
154
|
+
|
|
155
|
+
- Request logging
|
|
156
|
+
- Error tracking
|
|
157
|
+
- Debug information
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
@injectable()
|
|
161
|
+
export class RequestLogger
|
|
162
|
+
implements ExecutorPlugin<RequestAdapterFetchConfig>
|
|
163
|
+
{
|
|
164
|
+
readonly pluginName = 'RequestLogger';
|
|
165
|
+
|
|
166
|
+
onBefore(context) {
|
|
167
|
+
this.logger.log(`Request: ${context.parameters.url}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
onSuccess(context) {
|
|
171
|
+
this.logger.log(`Success: ${context.parameters.url}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
onError(context) {
|
|
175
|
+
this.logger.error(`Error: ${context.parameters.url}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 2. Custom Plugins
|
|
181
|
+
|
|
182
|
+
Creating custom plugins:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
export class CustomPlugin implements ExecutorPlugin<RequestAdapterFetchConfig> {
|
|
186
|
+
readonly pluginName = 'CustomPlugin';
|
|
187
|
+
|
|
188
|
+
onBefore(context) {
|
|
189
|
+
// Pre-request processing
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
onSuccess(context) {
|
|
193
|
+
// Success processing
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
onError(context) {
|
|
197
|
+
// Error processing
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Request Configuration
|
|
203
|
+
|
|
204
|
+
### 1. Basic Configuration
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
interface RequestConfig {
|
|
208
|
+
url: string;
|
|
209
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
210
|
+
headers?: Record<string, string>;
|
|
211
|
+
params?: Record<string, any>;
|
|
212
|
+
data?: any;
|
|
213
|
+
timeout?: number;
|
|
214
|
+
responseType?: 'json' | 'text' | 'blob';
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 2. Advanced Configuration
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
interface AdvancedConfig extends RequestConfig {
|
|
222
|
+
requestId?: string; // For request cancellation
|
|
223
|
+
mock?: boolean; // Enable mock data
|
|
224
|
+
retries?: number; // Number of retries
|
|
225
|
+
withCredentials?: boolean; // Cross-origin credentials
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Error Handling
|
|
230
|
+
|
|
231
|
+
### 1. Unified Error Handling
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
export class ApiCatchPlugin implements ExecutorPlugin {
|
|
235
|
+
onError(context) {
|
|
236
|
+
const error = context.error;
|
|
237
|
+
|
|
238
|
+
if (error.status === 401) {
|
|
239
|
+
// Handle authentication error
|
|
240
|
+
handleAuthError();
|
|
241
|
+
} else if (error.status === 500) {
|
|
242
|
+
// Handle server error
|
|
243
|
+
handleServerError();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### 2. Business Layer Error Handling
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
try {
|
|
253
|
+
const result = await api.getData();
|
|
254
|
+
handleSuccess(result);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
if (error instanceof ApiError) {
|
|
257
|
+
handleApiError(error);
|
|
258
|
+
} else {
|
|
259
|
+
handleUnexpectedError(error);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Best Practices
|
|
265
|
+
|
|
266
|
+
### 1. API Organization
|
|
267
|
+
|
|
268
|
+
- Organize APIs by functional modules
|
|
269
|
+
- Use unified response types
|
|
270
|
+
- Implement complete type definitions
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
// User-related APIs
|
|
274
|
+
export class UserApi extends RequestScheduler {
|
|
275
|
+
login(data: LoginData): Promise<LoginResponse> {}
|
|
276
|
+
register(data: RegisterData): Promise<RegisterResponse> {}
|
|
277
|
+
getUserInfo(): Promise<UserInfo> {}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Order-related APIs
|
|
281
|
+
export class OrderApi extends RequestScheduler {
|
|
282
|
+
createOrder(data: OrderData): Promise<OrderResponse> {}
|
|
283
|
+
getOrderList(): Promise<OrderList> {}
|
|
284
|
+
cancelOrder(id: string): Promise<void> {}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### 2. Request State Management
|
|
289
|
+
|
|
290
|
+
- Use Store to manage request states
|
|
291
|
+
- Implement loading state control
|
|
292
|
+
- Handle concurrent requests
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
interface RequestState<T> {
|
|
296
|
+
loading: boolean;
|
|
297
|
+
data: T | null;
|
|
298
|
+
error: Error | null;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
class DataStore extends StoreInterface<RequestState<Data>> {
|
|
302
|
+
async fetchData() {
|
|
303
|
+
if (this.state.loading) return;
|
|
304
|
+
|
|
305
|
+
this.setState({ loading: true });
|
|
306
|
+
try {
|
|
307
|
+
const data = await api.getData();
|
|
308
|
+
this.setState({ loading: false, data });
|
|
309
|
+
} catch (error) {
|
|
310
|
+
this.setState({ loading: false, error });
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### 3. Request Caching
|
|
317
|
+
|
|
318
|
+
- Implement request result caching
|
|
319
|
+
- Avoid duplicate requests
|
|
320
|
+
- Manage cache lifecycle
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
class CachePlugin implements ExecutorPlugin {
|
|
324
|
+
private cache = new Map();
|
|
325
|
+
|
|
326
|
+
onBefore(context) {
|
|
327
|
+
const cached = this.cache.get(context.parameters.url);
|
|
328
|
+
if (cached && !isExpired(cached)) {
|
|
329
|
+
return cached.data;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
onSuccess(context) {
|
|
334
|
+
this.cache.set(context.parameters.url, {
|
|
335
|
+
data: context.returnValue,
|
|
336
|
+
timestamp: Date.now()
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Debugging and Testing
|
|
343
|
+
|
|
344
|
+
### 1. Development Environment Configuration
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
// Development environment configuration
|
|
348
|
+
const devConfig = {
|
|
349
|
+
baseURL: '/api',
|
|
350
|
+
timeout: 5000,
|
|
351
|
+
mock: true,
|
|
352
|
+
debug: true
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
// Production environment configuration
|
|
356
|
+
const prodConfig = {
|
|
357
|
+
baseURL: 'https://api.example.com',
|
|
358
|
+
timeout: 10000,
|
|
359
|
+
mock: false,
|
|
360
|
+
debug: false
|
|
361
|
+
};
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### 2. Mock Data Configuration
|
|
365
|
+
|
|
366
|
+
```json
|
|
367
|
+
{
|
|
368
|
+
"GET /api/users": {
|
|
369
|
+
"code": 0,
|
|
370
|
+
"data": [
|
|
371
|
+
{ "id": 1, "name": "User 1" },
|
|
372
|
+
{ "id": 2, "name": "User 2" }
|
|
373
|
+
]
|
|
374
|
+
},
|
|
375
|
+
"POST /api/login": {
|
|
376
|
+
"code": 0,
|
|
377
|
+
"data": {
|
|
378
|
+
"token": "mock-token",
|
|
379
|
+
"user": { "id": 1, "name": "User" }
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### 3. Request Testing
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
describe('UserApi', () => {
|
|
389
|
+
let api: UserApi;
|
|
390
|
+
|
|
391
|
+
beforeEach(() => {
|
|
392
|
+
api = new UserApi(new MockAdapter());
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('should login successfully', async () => {
|
|
396
|
+
const result = await api.login({
|
|
397
|
+
username: 'test',
|
|
398
|
+
password: '123456'
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
expect(result.code).toBe(0);
|
|
402
|
+
expect(result.data.token).toBeDefined();
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## Performance Optimization
|
|
408
|
+
|
|
409
|
+
### 1. Request Optimization
|
|
410
|
+
|
|
411
|
+
- Implement request merging
|
|
412
|
+
- Control concurrency
|
|
413
|
+
- Optimize request timing
|
|
414
|
+
|
|
415
|
+
### 2. Caching Strategy
|
|
416
|
+
|
|
417
|
+
- Use caching judiciously
|
|
418
|
+
- Implement cache updates
|
|
419
|
+
- Manage cache size
|
|
420
|
+
|
|
421
|
+
### 3. Error Retry
|
|
422
|
+
|
|
423
|
+
- Configure retry strategies
|
|
424
|
+
- Handle network issues
|
|
425
|
+
- Avoid ineffective retries
|