@ametie/vue-muza-use 0.9.0 โ 0.9.1
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 +226 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ A production-ready composable that eliminates boilerplate and solves the hard pr
|
|
|
22
22
|
- ๐ **Batch Requests** โ Execute multiple requests in parallel with progress tracking
|
|
23
23
|
- ๐งน **Zero Memory Leaks** โ Automatic cleanup of pending requests on component unmount
|
|
24
24
|
- ๐ **ignoreUpdates** โ Atomic updates without triggering intermediate requests
|
|
25
|
+
- ๐๏ธ **Response Caching** โ In-memory cache with configurable TTL and manual invalidation
|
|
25
26
|
|
|
26
27
|
**Advanced Features** (When you need them):
|
|
27
28
|
- โป๏ธ **Intelligent Retries** โ Lifecycle-aware retry logic with configurable status codes
|
|
@@ -40,6 +41,7 @@ A production-ready composable that eliminates boilerplate and solves the hard pr
|
|
|
40
41
|
**Core Features:**
|
|
41
42
|
- [Watch & Auto-Refetch](#watch--auto-refetch)
|
|
42
43
|
- [ignoreUpdates โ Atomic Updates Without Refetch](#ignoreupdates--atomic-updates-without-refetch)
|
|
44
|
+
- [Response Caching](#response-caching)
|
|
43
45
|
- [Polling (Background Updates)](#polling-background-updates)
|
|
44
46
|
- [Error Handling](#error-handling)
|
|
45
47
|
- [retry โ Automatic Request Retry](#retry--automatic-request-retry)
|
|
@@ -499,6 +501,184 @@ ignoreUpdates(() => {
|
|
|
499
501
|
|
|
500
502
|
---
|
|
501
503
|
|
|
504
|
+
### Response Caching
|
|
505
|
+
|
|
506
|
+
**TL;DR: Pass `cache: 'key'` to serve repeated requests from memory instead of the network. Entries expire after 5 minutes by default.**
|
|
507
|
+
|
|
508
|
+
The cache is an in-memory `Map` shared across all `useApi` instances in the app.
|
|
509
|
+
It is intentionally simple: no reactive subscriptions, no persistence, no background timers.
|
|
510
|
+
Entries expire **lazily** โ stale entries are removed the next time they are read.
|
|
511
|
+
|
|
512
|
+
#### Basic Usage โ String Shorthand
|
|
513
|
+
|
|
514
|
+
```vue
|
|
515
|
+
<script setup lang="ts">
|
|
516
|
+
import { useApi } from '@ametie/vue-muza-use'
|
|
517
|
+
|
|
518
|
+
const { data, loading } = useApi<Category[]>('/categories', {
|
|
519
|
+
cache: 'categories', // uses DEFAULT_STALE_TIME (5 minutes)
|
|
520
|
+
immediate: true,
|
|
521
|
+
})
|
|
522
|
+
</script>
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
The first call hits the network and caches the result under the key `'categories'`.
|
|
526
|
+
Every subsequent `execute()` within 5 minutes is served from cache instantly โ `loading` never becomes `true` and no axios request is made.
|
|
527
|
+
|
|
528
|
+
#### Custom TTL โ CacheOptions Object
|
|
529
|
+
|
|
530
|
+
```vue
|
|
531
|
+
<script setup lang="ts">
|
|
532
|
+
import { useApi } from '@ametie/vue-muza-use'
|
|
533
|
+
|
|
534
|
+
const { data, execute } = useApi<Product[]>('/products', {
|
|
535
|
+
cache: {
|
|
536
|
+
id: 'products',
|
|
537
|
+
staleTime: 60_000, // 1 minute
|
|
538
|
+
},
|
|
539
|
+
immediate: true,
|
|
540
|
+
})
|
|
541
|
+
</script>
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
#### Cache Hit Behavior
|
|
545
|
+
|
|
546
|
+
When a valid cache entry is found:
|
|
547
|
+
|
|
548
|
+
| Property / Hook | Cache Hit |
|
|
549
|
+
|---|---|
|
|
550
|
+
| `loading` | stays `false` โ never set to `true` |
|
|
551
|
+
| `data` | updated immediately via `mutate()` |
|
|
552
|
+
| `onBefore` | **not called** |
|
|
553
|
+
| `onSuccess` | **not called** |
|
|
554
|
+
| `onFinish` | **not called** |
|
|
555
|
+
| axios request | **not made** |
|
|
556
|
+
|
|
557
|
+
This is intentional โ a cache hit is silent. If you need to know when data comes from cache vs the network, track it with `onSuccess` (only fires on network hits).
|
|
558
|
+
|
|
559
|
+
#### invalidateCache โ Bust Related Caches on Mutation
|
|
560
|
+
|
|
561
|
+
Use `invalidateCache` on a POST/PUT/DELETE to automatically clear caches when the mutation succeeds.
|
|
562
|
+
|
|
563
|
+
```vue
|
|
564
|
+
<script setup lang="ts">
|
|
565
|
+
import { useApi } from '@ametie/vue-muza-use'
|
|
566
|
+
|
|
567
|
+
// GET โ caches the list
|
|
568
|
+
const { data: products, execute: reload } = useApi<Product[]>('/products', {
|
|
569
|
+
cache: 'products',
|
|
570
|
+
immediate: true,
|
|
571
|
+
})
|
|
572
|
+
|
|
573
|
+
// POST โ busts the list cache on success so the next GET hits the network
|
|
574
|
+
const { execute: createProduct, loading } = useApi('/products', {
|
|
575
|
+
method: 'POST',
|
|
576
|
+
invalidateCache: 'products',
|
|
577
|
+
})
|
|
578
|
+
|
|
579
|
+
async function submit(form: NewProduct) {
|
|
580
|
+
await createProduct({ data: form })
|
|
581
|
+
await reload() // cache is gone โ fetches fresh data
|
|
582
|
+
}
|
|
583
|
+
</script>
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
`invalidateCache` fires **only on HTTP 2xx success**. It never runs in `catch` or `finally`.
|
|
587
|
+
Pass an array to bust multiple keys at once:
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
const { execute } = useApi('/orders', {
|
|
591
|
+
method: 'POST',
|
|
592
|
+
invalidateCache: ['orders', 'products', 'inventory'],
|
|
593
|
+
})
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
#### Imperative Cache Control
|
|
597
|
+
|
|
598
|
+
Import `invalidateCache` or `clearAllCache` anywhere in your app โ outside components, in Pinia stores, in route guards:
|
|
599
|
+
|
|
600
|
+
```typescript
|
|
601
|
+
import { invalidateCache, clearAllCache } from '@ametie/vue-muza-use'
|
|
602
|
+
|
|
603
|
+
// Bust a single key (e.g. after a WebSocket push)
|
|
604
|
+
invalidateCache('products')
|
|
605
|
+
|
|
606
|
+
// Bust multiple keys at once
|
|
607
|
+
invalidateCache(['products', 'categories'])
|
|
608
|
+
|
|
609
|
+
// Wipe everything โ call on logout to prevent data leaks between users
|
|
610
|
+
clearAllCache()
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
#### cache + watch
|
|
614
|
+
|
|
615
|
+
When `watch` is configured, each watch-triggered `execute()` still checks the cache first:
|
|
616
|
+
|
|
617
|
+
```vue
|
|
618
|
+
<script setup lang="ts">
|
|
619
|
+
import { useApi } from '@ametie/vue-muza-use'
|
|
620
|
+
import { ref } from 'vue'
|
|
621
|
+
|
|
622
|
+
const categoryId = ref<number>(1)
|
|
623
|
+
|
|
624
|
+
const { data } = useApi<Product[]>(() => `/categories/${categoryId.value}/products`, {
|
|
625
|
+
cache: { id: `products-cat-${categoryId.value}`, staleTime: 30_000 },
|
|
626
|
+
watch: categoryId,
|
|
627
|
+
immediate: true,
|
|
628
|
+
})
|
|
629
|
+
</script>
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
> [!NOTE]
|
|
633
|
+
> The cache `id` is evaluated once when `useApi` is called. To cache per category,
|
|
634
|
+
> use a computed or a dynamic key string derived from your reactive state.
|
|
635
|
+
|
|
636
|
+
#### cache + retry
|
|
637
|
+
|
|
638
|
+
Cache is written **after the final successful attempt**, not after the first.
|
|
639
|
+
If the first attempt fails and a retry succeeds, the retry's response is cached:
|
|
640
|
+
|
|
641
|
+
```typescript
|
|
642
|
+
const { data } = useApi('/reports', {
|
|
643
|
+
cache: 'reports',
|
|
644
|
+
retry: 2,
|
|
645
|
+
retryStatusCodes: [500, 503],
|
|
646
|
+
immediate: true,
|
|
647
|
+
})
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
#### Complete Options Reference
|
|
651
|
+
|
|
652
|
+
| Option | Type | Default | Description |
|
|
653
|
+
|--------|------|---------|-------------|
|
|
654
|
+
| `cache` | `string \| CacheOptions` | `undefined` | Enable caching. String = `{ id, staleTime: 300_000 }` shorthand |
|
|
655
|
+
| `invalidateCache` | `string \| string[]` | `undefined` | Cache key(s) to delete on 2xx success |
|
|
656
|
+
|
|
657
|
+
**`CacheOptions`**
|
|
658
|
+
|
|
659
|
+
| Field | Type | Default | Description |
|
|
660
|
+
|-------|------|---------|-------------|
|
|
661
|
+
| `id` | `string` | โ | Unique cache key |
|
|
662
|
+
| `staleTime` | `number` | `300_000` | TTL in milliseconds. Entry is deleted on next read after this time |
|
|
663
|
+
|
|
664
|
+
#### Out of Scope (by design)
|
|
665
|
+
|
|
666
|
+
The following are intentionally **not** supported in v1:
|
|
667
|
+
|
|
668
|
+
- ๐ซ No reactive cache entries โ the cache is a plain `Map`, not Vue refs
|
|
669
|
+
- ๐ซ No `localStorage` / `sessionStorage` persistence
|
|
670
|
+
- ๐ซ No background TTL timers โ expiry is checked lazily on read
|
|
671
|
+
- ๐ซ No cache for `useApiBatch` โ batch requests manage their own state
|
|
672
|
+
- ๐ซ No automatic refetch on cache invalidation โ call `execute()` manually after invalidating
|
|
673
|
+
- ๐ซ No request deduplication โ concurrent calls for the same key each fire their own request
|
|
674
|
+
|
|
675
|
+
> [!WARNING]
|
|
676
|
+
> The cache store is **module-level** (a singleton). In SSR / Node.js environments it is
|
|
677
|
+
> shared between all incoming requests. Call `clearAllCache()` between requests or avoid
|
|
678
|
+
> using caching in SSR contexts.
|
|
679
|
+
|
|
680
|
+
---
|
|
681
|
+
|
|
502
682
|
### Polling (Background Updates)
|
|
503
683
|
|
|
504
684
|
**TL;DR: Keep data fresh with smart polling that automatically pauses when the browser tab is hidden.**
|
|
@@ -1511,6 +1691,13 @@ function useMyCustomComposable<T>(fetchFn: () => Promise<T>) {
|
|
|
1511
1691
|
| `watch` | `WatchSource \| WatchSource[]` | `undefined` | One or more refs to watch โ request re-fires when any of them change |
|
|
1512
1692
|
| `debounce` | `number` | `0` | Milliseconds to wait after the last watch change before firing the request |
|
|
1513
1693
|
|
|
1694
|
+
**Caching:**
|
|
1695
|
+
|
|
1696
|
+
| Option | Type | Default | Description |
|
|
1697
|
+
|--------|------|---------|-------------|
|
|
1698
|
+
| `cache` | `string \| CacheOptions` | `undefined` | Cache the response in memory. String shorthand uses default 5-min TTL. See [Response Caching](#response-caching) |
|
|
1699
|
+
| `invalidateCache` | `string \| string[]` | `undefined` | Cache key(s) to delete on 2xx success. Never fires on error |
|
|
1700
|
+
|
|
1514
1701
|
**Polling:**
|
|
1515
1702
|
|
|
1516
1703
|
| Option | Type | Default | Description |
|
|
@@ -1768,6 +1955,45 @@ const { data, loading, error, mutate, setLoading, setError, reset } =
|
|
|
1768
1955
|
|
|
1769
1956
|
---
|
|
1770
1957
|
|
|
1958
|
+
### `invalidateCache(id)` / `clearAllCache()`
|
|
1959
|
+
|
|
1960
|
+
**TL;DR: Imperatively delete one, many, or all cache entries from anywhere in your app.**
|
|
1961
|
+
|
|
1962
|
+
```typescript
|
|
1963
|
+
import { invalidateCache, clearAllCache } from '@ametie/vue-muza-use'
|
|
1964
|
+
```
|
|
1965
|
+
|
|
1966
|
+
| Function | Signature | Description |
|
|
1967
|
+
|----------|-----------|-------------|
|
|
1968
|
+
| `invalidateCache` | `(id: string \| string[]) => void` | Delete one or more cache entries by key |
|
|
1969
|
+
| `clearAllCache` | `() => void` | Wipe the entire cache โ use on logout |
|
|
1970
|
+
|
|
1971
|
+
**Example โ bust cache after a WebSocket push:**
|
|
1972
|
+
|
|
1973
|
+
```typescript
|
|
1974
|
+
// pinia store or composable outside a component
|
|
1975
|
+
import { invalidateCache } from '@ametie/vue-muza-use'
|
|
1976
|
+
|
|
1977
|
+
socket.on('products:updated', () => {
|
|
1978
|
+
invalidateCache('products')
|
|
1979
|
+
})
|
|
1980
|
+
```
|
|
1981
|
+
|
|
1982
|
+
**Example โ clear all on logout:**
|
|
1983
|
+
|
|
1984
|
+
```typescript
|
|
1985
|
+
import { clearAllCache } from '@ametie/vue-muza-use'
|
|
1986
|
+
import { tokenManager } from '@ametie/vue-muza-use'
|
|
1987
|
+
|
|
1988
|
+
function logout() {
|
|
1989
|
+
tokenManager.clearTokens()
|
|
1990
|
+
clearAllCache() // prevent stale data from leaking to the next user session
|
|
1991
|
+
router.push('/login')
|
|
1992
|
+
}
|
|
1993
|
+
```
|
|
1994
|
+
|
|
1995
|
+
---
|
|
1996
|
+
|
|
1771
1997
|
## ๐งฉ Common Patterns
|
|
1772
1998
|
|
|
1773
1999
|
### 1. Search with Debounce and Reset
|