nuxt-async-cache
v0.0.14
Published
Caching system for Nuxt with IndexedDB, automatic refetching, and cross-tab synchronization
Readme
Nuxt Async Cache
Caching system for Nuxt with IndexedDB, automatic refetching, and cross-tab synchronization.
- SSR-enabled: works with Nuxt SSR
- IndexedDB Storage: persistent client-side caching using Dexie
- Auto-refresh: flexible data refreshing
- Cross-tab Sync: coordinated data updates across multiple tabs
- Lock Coordination: de-duplicated requests across tabs
Installation
1. Install the package
# Using pnpm (recommended)
pnpm add nuxt-async-cache
# Using npm
npm install nuxt-async-cache
# Using yarn
yarn add nuxt-async-cache2. Add to Nuxt modules
Add the module to your nuxt.config.ts:
export default defineNuxtConfig({
modules: [
'nuxt-async-cache'
],
// Optional: Configure module options
asyncCache: {
// Module configuration options (if any)
}
})Note: The module automatically configures Vite to handle the Dexie dependency correctly. If you encounter import issues, you can add this fallback configuration:
export default defineNuxtConfig({
modules: ['nuxt-async-cache'],
vite: {
resolve: {
alias: {
'dexie': 'dexie/dist/dexie.mjs'
}
}
}
})3. Start using the composable
The module automatically provides the useCachedAsyncData composable across your entire Nuxt application:
<script setup>
// No imports needed - auto-imported by the module
const { data, pending } = useCachedAsyncData({
key: 'my-data',
handler: () => $fetch('/api/data')
})
</script>Basic Usage
<script setup>
const { data, pending, error, refresh, clear } = useCachedAsyncData({
key: 'user-profile',
handler: () => $fetch('/api/user/profile'),
defaults: {
staleAfter: 60000, // 1 minute
refetchAfter: 300000, // 5 minutes
expiresAfter: 3600000 // 1 hour
}
})
</script>
<template>
<div>
<div v-if="pending">Loading...</div>
<div v-else-if="error">Error: {{ error.message }}</div>
<div v-else-if="data">
<h1>{{ data.name }}</h1>
<button @click="refresh()">Refresh</button>
<button @click="clear()">Clear Cache</button>
</div>
</div>
</template>Reactive Keys
The key parameter supports reactive values, allowing you to dynamically change the cache key:
<script setup>
const userId = ref('123')
const userType = ref('admin')
// Reactive key that updates when dependencies change
const { data, pending } = useCachedAsyncData({
key: computed(() => `user-${userId.value}-${userType.value}`),
handler: () => $fetch(`/api/users/${userId.value}?type=${userType.value}`)
})
// When userId or userType changes, the cache key changes
// and data is automatically refetched for the new key
function switchUser(newId, newType) {
userId.value = newId
userType.value = newType
// Data will automatically update for new cache key
}
</script>You can also use refs directly:
<script setup>
const cacheKey = ref('initial-key')
const { data } = useCachedAsyncData({
key: cacheKey, // Reactive ref
handler: () => $fetch('/api/data')
})
// Changing the key will switch to different cached data
cacheKey.value = 'different-key'
</script>Advanced Usage with Dynamic Timestamps
const { data } = useCachedAsyncData({
key: 'api-data',
handler: () => $fetch('/api/data'),
timestamps: {
// Use server-provided timestamp
requestTime: (response) => response.timestamp,
// Compute staleness based on data type
staleAfter: (response) => {
if (response.type === 'critical') return 10000 // 10 seconds
if (response.type === 'normal') return 60000 // 1 minute
return 300000 // 5 minutes for others
},
// Auto-refresh based on server hint
refetchAfter: (response) => response.nextUpdateIn || null,
// Expiry based on data importance
expiresAfter: (response) => response.ttl || 86400000
}
})API Reference
useCachedAsyncData(options)
Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| key | MaybeRefOrGetter<string> | Required | Unique cache key (supports reactive values) |
| handler | () => Promise<T> | Required | Data fetching function |
| timestamps | TimestampConfig<T> | {} | Dynamic timestamp configuration |
| defaults | DefaultTimestamps | See below | Default timing values |
| immediate | boolean | true | Fetch immediately |
| server | boolean | true | Fetch on server |
| lazy | boolean | false | Don't block navigation |
| dedupe | 'defer' \| 'cancel' | undefined | Deduplication strategy |
Default Timestamps
{
staleAfter: 30000, // 30 seconds
refetchAfter: null, // No auto-refetch
expiresAfter: 86400000 // 24 hours
}Return Value
{
data: Ref<T | null>, // Reactive data
pending: Ref<boolean>, // Loading state
error: Ref<Error | null>, // Error state
status: Ref<Status>, // 'idle' | 'pending' | 'success' | 'error'
refresh: () => Promise<void>, // Manual refresh
clear: () => Promise<void> // Clear cache entry
}Cache Management Utilities
The module provides client-side utilities for cache management:
// In a Nuxt plugin or client composable
import {
cleanupExpiredCache,
clearAllCache,
setupCacheMaintenance
} from 'nuxt-async-cache/runtime/utils/cache'
// Manual cleanup (client-side only)
await cleanupExpiredCache()
// Clear everything
await clearAllCache()
// Auto maintenance (runs every 5 minutes by default, client-side only)
const stopMaintenance = setupCacheMaintenance()
// Call stopMaintenance() to stopHow It Works
1. SSR Phase
- Data is fetched on the server and stored in memory
- Cache entries are passed to the client via Nuxt payload
2. Client Hydration
- Client checks IndexedDB for newer data
- Uses server data if local cache is older or missing
- Sets up auto-refresh timers based on timestamps
3. Cross-Tab Coordination
- Uses Web Locks API for coordinating refresh across tabs
- Falls back to Dexie-based locking if Web Locks unavailable
- BroadcastChannel notifies other tabs of cache updates
4. Smart Refreshing
- Stale: Data is shown but background refresh is triggered
- Refetch: Automatic refresh at specified intervals
- Expired: Data is removed from cache entirely
Best Practices
- Choose appropriate cache keys: Use descriptive, unique keys. Keys can be static strings, reactive refs, or computed values for dynamic caching
- Set reasonable timeouts: Balance freshness vs performance
- Handle errors gracefully: Always provide error states
- Use dynamic timestamps: Leverage server hints when available
- Monitor cache size: Use cleanup utilities in production
- Leverage reactive keys: Use computed keys for dynamic data that depends on user state or route parameters
Browser Support
- IndexedDB: All modern browsers
- Web Locks API: Chrome 69+, Firefox 96+, Safari 15.4+
- BroadcastChannel: All modern browsers
Graceful fallbacks are provided for unsupported APIs.
Development
Setup
# Clone the repository
git clone <repository-url>
cd nuxt-async-cache
# Install dependencies
pnpm install
# Prepare the module
pnpm dev:prepare
# Start development server
pnpm devTesting
# Run tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Run type checking
pnpm test:typesBuilding
# Build the module
pnpm prepack
# Lint code
pnpm lint