@dxtmisha/wiki 0.24.0 → 0.24.2

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.
@@ -0,0 +1,517 @@
1
+ import {Meta} from '@storybook/addon-docs/blocks'
2
+
3
+ <Meta title='@dxtmisha/functional/en/Composables/useApiRef'/>
4
+
5
+ # Composable useApiRef
6
+
7
+ Composable for working with HTTP requests in Vue 3 applications. Provides reactive interface for executing API requests with automatic loading state management, reactive parameters support, conditional execution, and data transformation.
8
+
9
+ ## Key Features
10
+
11
+ - **Reactive requests** — automatic request execution when parameters change
12
+ - **State management** — automatic tracking of loading state and data readiness
13
+ - **Conditional execution** — ability to execute requests only when conditions are met
14
+ - **Data transformation** — transform received data before storing
15
+ - **Api integration** — uses Api class for request execution with caching support
16
+ - **Global conditions** — ability to set global conditions for all useApiRef instances
17
+ - **Auto cleanup** — optional data cleanup on component unmount
18
+ - **TypeScript support** — full typing with generics
19
+
20
+ ## Basic Usage
21
+
22
+ ### Simple GET Request
23
+
24
+ ```javascript
25
+ import { useApiRef } from '@dxtmisha/functional'
26
+
27
+ // Executes GET request on component mount
28
+ const { data, loading, isStarting, reading } = useApiRef('/api/users')
29
+
30
+ // data - ref with response data
31
+ // loading - ref with loading state
32
+ // isStarting - computed, true before first data load
33
+ // reading - computed, true while reading data
34
+ ```
35
+
36
+ ### Component Usage
37
+
38
+ ```vue
39
+ <script setup>
40
+ import { useApiRef } from '@dxtmisha/functional'
41
+
42
+ const { data: users, loading } = useApiRef('/api/users')
43
+ </script>
44
+
45
+ <template>
46
+ <div v-if="loading">Loading...</div>
47
+ <div v-else-if="users">
48
+ <div v-for="user in users" :key="user.id">
49
+ {{ user.name }}
50
+ </div>
51
+ </div>
52
+ </template>
53
+ ```
54
+
55
+ ## Parameters
56
+
57
+ ### `path`
58
+
59
+ Path to API endpoint. Can be a string or ref for reactivity.
60
+
61
+ **Type:** `RefOrNormal<string | undefined>`
62
+
63
+ ```javascript
64
+ // Static path
65
+ const { data } = useApiRef('/api/users')
66
+
67
+ // Reactive path
68
+ const userId = ref(1)
69
+ const { data: user } = useApiRef(computed(() => `/api/users/${userId.value}`))
70
+
71
+ // New request executes automatically when userId changes
72
+ userId.value = 2
73
+ ```
74
+
75
+ ### `options`
76
+
77
+ Request parameters. Can be HTTP method, settings object, or ref.
78
+
79
+ **Type:** `ApiMethodItem | RefOrNormal<ApiFetch>`
80
+
81
+ ```javascript
82
+ // Specify method only
83
+ const { data } = useApiRef('/api/users', 'POST')
84
+
85
+ // Full settings
86
+ const { data } = useApiRef('/api/users', {
87
+ method: 'POST',
88
+ request: { name: 'John', email: 'john@example.com' },
89
+ headers: { 'Content-Type': 'application/json' }
90
+ })
91
+
92
+ // Reactive parameters
93
+ const params = ref({ page: 1, limit: 10 })
94
+ const { data } = useApiRef('/api/users', computed(() => ({
95
+ method: 'GET',
96
+ request: params.value
97
+ })))
98
+
99
+ // New request on params change
100
+ params.value = { page: 2, limit: 10 }
101
+ ```
102
+
103
+ ### `reactivity`
104
+
105
+ Enable or disable automatic execution on parameter changes.
106
+
107
+ **Type:** `boolean`
108
+ **Default:** `true`
109
+
110
+ ```javascript
111
+ // With reactivity (default)
112
+ const params = ref({ page: 1 })
113
+ const { data, reset } = useApiRef('/api/users', { request: params }, true)
114
+
115
+ params.value.page = 2 // Request executes automatically
116
+
117
+ // Without reactivity
118
+ const { data, reset } = useApiRef('/api/users', { request: params }, false)
119
+
120
+ params.value.page = 2 // Request does NOT execute
121
+ await reset() // Need to call reset manually
122
+ ```
123
+
124
+ ### `conditions`
125
+
126
+ Condition for request execution. Request executes only if condition is true.
127
+
128
+ **Type:** `RefType<boolean>`
129
+
130
+ ```javascript
131
+ // Request executes only when isAuth === true
132
+ const isAuth = ref(false)
133
+ const { data } = useApiRef('/api/profile', undefined, true, isAuth)
134
+
135
+ // data will be undefined while isAuth === false
136
+ console.log(data.value) // undefined
137
+
138
+ isAuth.value = true // Request executes automatically
139
+ ```
140
+
141
+ ### `transformation`
142
+
143
+ Function to transform received data before storing in ref.
144
+
145
+ **Type:** `(data: T) => R`
146
+
147
+ ```javascript
148
+ // Transform API data to needed format
149
+ const { data } = useApiRef(
150
+ '/api/users',
151
+ undefined,
152
+ true,
153
+ undefined,
154
+ (response) => {
155
+ // response - data from API
156
+ // Return transformed data
157
+ return response.data.map(user => ({
158
+ id: user.id,
159
+ fullName: `${user.firstName} ${user.lastName}`,
160
+ isActive: user.status === 'active'
161
+ }))
162
+ }
163
+ )
164
+
165
+ // data contains transformed data
166
+ ```
167
+
168
+ ### `unmounted`
169
+
170
+ Whether to clear data on component unmount.
171
+
172
+ **Type:** `boolean`
173
+ **Default:** `undefined`
174
+
175
+ ```javascript
176
+ // Data will be cleared on component unmount
177
+ const { data } = useApiRef('/api/users', undefined, true, undefined, undefined, true)
178
+
179
+ // On page navigation, data becomes undefined
180
+ ```
181
+
182
+ ## Return Values
183
+
184
+ ### `data`
185
+
186
+ Ref with response data. `undefined` before first load.
187
+
188
+ **Type:** `Ref<R | undefined>`
189
+
190
+ ```javascript
191
+ const { data } = useApiRef('/api/users')
192
+
193
+ console.log(data.value) // undefined before load
194
+ // After load: array of users
195
+ ```
196
+
197
+ ### `loading`
198
+
199
+ Ref with loading state. `true` during request execution.
200
+
201
+ **Type:** `Ref<boolean>`
202
+
203
+ ```javascript
204
+ const { loading } = useApiRef('/api/users')
205
+
206
+ console.log(loading.value) // true during load, false after
207
+ ```
208
+
209
+ ### `isStarting`
210
+
211
+ Computed showing if data has been loaded at least once. `true` before first load.
212
+
213
+ **Type:** `ComputedRef<boolean>`
214
+
215
+ ```javascript
216
+ const { data, isStarting } = useApiRef('/api/users')
217
+
218
+ console.log(isStarting.value) // true before first load
219
+ // After first load always false, even on subsequent requests
220
+ ```
221
+
222
+ ### `reading`
223
+
224
+ Computed showing active data reading.
225
+
226
+ **Type:** `ComputedRef<boolean>`
227
+
228
+ ```javascript
229
+ const { reading } = useApiRef('/api/users')
230
+
231
+ console.log(reading.value) // true while reading data
232
+ ```
233
+
234
+ ### `reset`
235
+
236
+ Function to force request execution.
237
+
238
+ **Type:** `() => Promise<void>`
239
+
240
+ ```javascript
241
+ const { data, reset } = useApiRef('/api/users')
242
+
243
+ // Force reload data
244
+ await reset()
245
+ ```
246
+
247
+ ## Usage Examples
248
+
249
+ ### List with Filters
250
+
251
+ ```vue
252
+ <script setup>
253
+ import { ref, computed } from 'vue'
254
+ import { useApiRef } from '@dxtmisha/functional'
255
+
256
+ const searchQuery = ref('')
257
+ const currentPage = ref(1)
258
+
259
+ const { data: users, loading } = useApiRef(
260
+ '/api/users',
261
+ computed(() => ({
262
+ method: 'GET',
263
+ request: {
264
+ search: searchQuery.value,
265
+ page: currentPage.value,
266
+ limit: 20
267
+ }
268
+ }))
269
+ )
270
+
271
+ const handleSearch = (query) => {
272
+ searchQuery.value = query
273
+ currentPage.value = 1 // Reset to first page
274
+ // Request executes automatically
275
+ }
276
+ </script>
277
+
278
+ <template>
279
+ <div>
280
+ <input v-model="searchQuery" placeholder="Search...">
281
+ <div v-if="loading">Loading...</div>
282
+ <div v-else>
283
+ <div v-for="user in users" :key="user.id">{{ user.name }}</div>
284
+ <button @click="currentPage++">Next page</button>
285
+ </div>
286
+ </div>
287
+ </template>
288
+ ```
289
+
290
+ ### Conditional Loading
291
+
292
+ ```vue
293
+ <script setup>
294
+ import { ref } from 'vue'
295
+ import { useApiRef } from '@dxtmisha/functional'
296
+
297
+ const showDetails = ref(false)
298
+ const userId = ref(1)
299
+
300
+ // Data loads only when showDetails === true
301
+ const { data: userDetails, loading } = useApiRef(
302
+ computed(() => `/api/users/${userId.value}/details`),
303
+ undefined,
304
+ true,
305
+ showDetails
306
+ )
307
+
308
+ const toggleDetails = () => {
309
+ showDetails.value = !showDetails.value
310
+ // If showDetails becomes true, request executes automatically
311
+ }
312
+ </script>
313
+
314
+ <template>
315
+ <button @click="toggleDetails">
316
+ {{ showDetails ? 'Hide' : 'Show' }} details
317
+ </button>
318
+ <div v-if="showDetails">
319
+ <div v-if="loading">Loading details...</div>
320
+ <div v-else-if="userDetails">{{ userDetails }}</div>
321
+ </div>
322
+ </template>
323
+ ```
324
+
325
+ ### POST Request with Transformation
326
+
327
+ ```vue
328
+ <script setup>
329
+ import { ref } from 'vue'
330
+ import { useApiRef } from '@dxtmisha/functional'
331
+
332
+ const formData = ref({
333
+ firstName: '',
334
+ lastName: '',
335
+ email: ''
336
+ })
337
+
338
+ const { data: createdUser, loading, reset } = useApiRef(
339
+ '/api/users',
340
+ {
341
+ method: 'POST',
342
+ request: formData
343
+ },
344
+ false, // Disable automatic execution
345
+ undefined,
346
+ (response) => {
347
+ // Transform API response
348
+ return {
349
+ id: response.id,
350
+ fullName: `${response.firstName} ${response.lastName}`,
351
+ email: response.email,
352
+ createdAt: new Date(response.created_at)
353
+ }
354
+ }
355
+ )
356
+
357
+ const handleSubmit = async () => {
358
+ await reset() // Execute request manually
359
+ if (createdUser.value) {
360
+ console.log('User created:', createdUser.value)
361
+ }
362
+ }
363
+ </script>
364
+
365
+ <template>
366
+ <form @submit.prevent="handleSubmit">
367
+ <input v-model="formData.firstName" placeholder="First name">
368
+ <input v-model="formData.lastName" placeholder="Last name">
369
+ <input v-model="formData.email" placeholder="Email">
370
+ <button :disabled="loading">
371
+ {{ loading ? 'Submitting...' : 'Create' }}
372
+ </button>
373
+ </form>
374
+ </template>
375
+ ```
376
+
377
+ ### Multiple Requests
378
+
379
+ ```vue
380
+ <script setup>
381
+ import { useApiRef } from '@dxtmisha/functional'
382
+
383
+ const { data: users, loading: usersLoading } = useApiRef('/api/users')
384
+ const { data: posts, loading: postsLoading } = useApiRef('/api/posts')
385
+ const { data: comments, loading: commentsLoading } = useApiRef('/api/comments')
386
+
387
+ const isLoading = computed(() =>
388
+ usersLoading.value || postsLoading.value || commentsLoading.value
389
+ )
390
+ </script>
391
+
392
+ <template>
393
+ <div v-if="isLoading">Loading data...</div>
394
+ <div v-else>
395
+ <section>
396
+ <h2>Users</h2>
397
+ <div v-for="user in users" :key="user.id">{{ user.name }}</div>
398
+ </section>
399
+ <section>
400
+ <h2>Posts</h2>
401
+ <div v-for="post in posts" :key="post.id">{{ post.title }}</div>
402
+ </section>
403
+ <section>
404
+ <h2>Comments</h2>
405
+ <div v-for="comment in comments" :key="comment.id">{{ comment.text }}</div>
406
+ </section>
407
+ </div>
408
+ </template>
409
+ ```
410
+
411
+ ## Global Conditions
412
+
413
+ The `setApiRefGlobalConditions` function allows setting a global condition for all useApiRef instances.
414
+
415
+ ```javascript
416
+ import { setApiRefGlobalConditions, useApiRef } from '@dxtmisha/functional'
417
+ import { ref } from 'vue'
418
+
419
+ // Global condition - e.g., authentication status
420
+ const isAuthenticated = ref(false)
421
+ setApiRefGlobalConditions(isAuthenticated)
422
+
423
+ // Now all useApiRef will consider this condition
424
+ const { data: profile } = useApiRef('/api/profile')
425
+ const { data: settings } = useApiRef('/api/settings')
426
+
427
+ // Requests won't execute while isAuthenticated === false
428
+ isAuthenticated.value = true // All requests execute automatically
429
+ ```
430
+
431
+ ## Integration with Api Class
432
+
433
+ useApiRef uses the Api class, so all Api settings apply automatically:
434
+
435
+ ```javascript
436
+ import { Api, useApiRef } from '@dxtmisha/functional'
437
+
438
+ // Configure base URL and headers
439
+ Api.setUrl('/api/v1/')
440
+ Api.setHeaders({
441
+ 'Authorization': 'Bearer token123',
442
+ 'Accept': 'application/json'
443
+ })
444
+
445
+ // useApiRef automatically uses these settings
446
+ const { data } = useApiRef('/users') // Request to /api/v1/users with headers
447
+ ```
448
+
449
+ ## TypeScript
450
+
451
+ ```typescript
452
+ interface User {
453
+ id: number
454
+ name: string
455
+ email: string
456
+ }
457
+
458
+ interface ApiResponse<T> {
459
+ data: T[]
460
+ total: number
461
+ }
462
+
463
+ // Type response data
464
+ const { data } = useApiRef<User[]>('/api/users')
465
+
466
+ // Type with transformation
467
+ const { data } = useApiRef<User[], ApiResponse<User>>(
468
+ '/api/users',
469
+ undefined,
470
+ true,
471
+ undefined,
472
+ (response) => response.data // response typed as ApiResponse<User>
473
+ )
474
+ ```
475
+
476
+ ## Behavior Features
477
+
478
+ ### Lazy Initialization
479
+
480
+ Request doesn't execute until first access to `data`:
481
+
482
+ ```javascript
483
+ const api = useApiRef('/api/users')
484
+
485
+ // Request not executed yet
486
+ console.log('Composable created')
487
+
488
+ // Request executes on first access to data
489
+ console.log(api.data.value)
490
+ ```
491
+
492
+ ### Automatic Reactivity
493
+
494
+ When using ref or computed in parameters, requests execute automatically:
495
+
496
+ ```javascript
497
+ const userId = ref(1)
498
+ const { data } = useApiRef(computed(() => `/api/users/${userId.value}`))
499
+
500
+ // Each userId change triggers a new request
501
+ userId.value = 2 // Request to /api/users/2
502
+ userId.value = 3 // Request to /api/users/3
503
+ ```
504
+
505
+ ### Preventing Duplicates
506
+
507
+ If a request is already running, a new request won't start:
508
+
509
+ ```javascript
510
+ const { reset, loading } = useApiRef('/api/users')
511
+
512
+ await reset() // First request
513
+ // loading.value === true
514
+
515
+ await reset() // This call will be ignored until the first completes
516
+ ```
517
+