- Notifications
You must be signed in to change notification settings - Fork75
Stale-while-revalidate data fetching for Vue
License
Kong/swrv
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
swrv
(pronounced "swerve") is a library using theVue Composition API for remote data fetching. It is largely a port ofswr.
The name “SWR” is derived from stale-while-revalidate, a cache invalidation strategy popularized by HTTPRFC 5861. SWR first returns the data from cache (stale), then sends the fetch request (revalidate), and finally comes with the up-to-date data again.
Features:
- Transport and protocol agnostic data fetching
- Fast page navigation
- Interval polling
SSR support(removed as of version0.10.0
-read more)- Vue 3 Support
- Revalidation on focus
- Request deduplication
- TypeScript ready
- Minimal API
- Stale-if-error
- Customizable cache implementation
- Error Retry
Withswrv
, components will get a stream of data updates constantly and automatically. Thus, the UI will be always fast and reactive.
- Installation
- Getting Started
- Api
- Prefetching
- Dependent Fetching
- Stale-if-error
- State Management
- Cache
- Error Handling
- FAQ
- Contributors ✨
The version ofswrv
you install depends on the Vue dependency in your project.
# Install the latest versionyarn add swrv
This version removes the dependency of the external@vue/composition-api
plugin and addsvue
to thepeerDependencies
, requiring a version that matches the following pattern:>= 2.7.0 < 3
# Install the 0.10.x version for Vue 2.7yarn add swrv@v2-latest
If you're installing for Vue2.6.x
and below, you may want to check out aprevious version of the README to view how to initializeswrv
utilizing the external@vue/composition-api
plugin.
# Install the 0.9.x version for Vue < 2.7yarn add swrv@legacy
<template> <div> <divv-if="error">failed to load</div> <divv-if="!data">loading...</div> <divv-else>hello {{ data.name }}</div> </div></template><script>importuseSWRVfrom'swrv'exportdefault { name:'Profile',setup() {const {data,error }=useSWRV('/api/user', fetcher)return { data, error, } },}</script>
In this example, the Vue HookuseSWRV
accepts akey
and afetcher
function.key
is a unique identifier of the request, normally the URL of the API. And the fetcher accepts key as its parameter and returns the data asynchronously.
useSWRV
also returns 2 values:data
anderror
. When the request (fetcher) is not yet finished, data will beundefined
. And when we get a response, it setsdata
anderror
based on the result of fetcher and rerenders the component. This is becausedata
anderror
are VueRefs, and their values will be set by the fetcher response.
Note that fetcher can be any asynchronous function, so you can use your favorite data-fetching library to handle that part. When omitted, swrv falls back to the browserFetch API.
const{ data, error, isValidating, mutate}=useSWRV(key,fetcher,options)
Param | Required | Description |
---|---|---|
key | yes | a unique key string for the request (or a reactive reference / watcher function / null) (advanced usage) |
fetcher | a Promise returning function to fetch your data. Ifnull , swrv will fetch from cache only and not revalidate. If omitted (i.e.undefined ) then thefetch api will be used. | |
options | an object of configuration options |
data
: data for the given key resolved by fetcher (or undefined if notloaded)error
: error thrown by fetcher (or undefined)isValidating
: if there's a request or revalidation loadingmutate
: function to trigger the validation manually
refreshInterval = 0
- polling interval in milliseconds. 0 means this isdisabled.dedupingInterval = 2000
- dedupe requests with the same key in this timespanttl = 0
- time to live of response data in cache. 0 mean it stays aroundforever.shouldRetryOnError = true
- retry when fetcher has an errorerrorRetryInterval = 5000
- error retry intervalerrorRetryCount: 5
- max error retry countrevalidateOnFocus = true
- auto revalidate when window gets focusedrevalidateDebounce = 0
- debounce in milliseconds for revalidation. Usefulfor when a component is serving from the cache immediately, but then un-mountssoon thereafter (e.g. a user clicking "next" in pagination quickly) to avoidunnecessary fetches.cache
- caching instance to store response data in. Seesrc/lib/cache, andCache below.
Prefetching can be useful for when you anticipate user actions, like hovering over a link. SWRV exposes themutate
function so that results can be stored in the SWRV cache at a predetermined time.
import{mutate}from'swrv'functionprefetch(){mutate('/api/data',fetch('/api/data').then((res)=>res.json()))// the second parameter is a Promise// SWRV will use the result when it resolves}
swrv also allows you to fetch data that depends on other data. It ensures the maximum possible parallelism (avoiding waterfalls), as well as serial fetching when a piece of dynamic data is required for the next data fetch to happen.
<template> <pv-if="!projects">loading...</p> <pv-else>You have {{ projects.length }} projects</p></template><script>import {ref }from'vue'importuseSWRVfrom'swrv'exportdefault { name:'Profile',setup() {const { data:user }=useSWRV('/api/user', fetch)const { data:projects }=useSWRV(()=>user.value&&'/api/projects?uid='+user.value.id, fetch)// if the return value of the cache key function is falsy, the fetcher// will not trigger, but since `user` is inside the cache key function,// it is being watched so when it is available, then the projects will// be fetched.return { user, projects } },}</script>
One of the benefits of a stale content caching strategy is that the cache can be served when requests fail.swrv
uses astale-if-error strategy and will maintaindata
in the cache even if auseSWRV
fetch returns anerror
.
<template> <divv-if="error">failed to load</div> <divv-if="data === undefined && !error">loading...</div> <pv-if="data"> hello {{ data.name }} of {{ data.birthplace }}. This content will continue to appear even if future requests to {{ endpoint }} fail! </p></template><script>import {ref }from'vue'importuseSWRVfrom'swrv'exportdefault { name:'Profile',setup() {constendpoint=ref('/api/user/Geralt')const {data,error }=useSWRV(endpoint.value, fetch)return { endpoint, data, error, } },}</script>
Sometimes you might want to know the exact state where swrv is during stale-while-revalidate lifecyle. This is helpful when representing the UI as a function of state. Here is one way to detect state using a user-land composableuseSwrvState
function:
import{ref,watchEffect}from'vue'constSTATES={VALIDATING:'VALIDATING',PENDING:'PENDING',SUCCESS:'SUCCESS',ERROR:'ERROR',STALE_IF_ERROR:'STALE_IF_ERROR',}exportdefaultfunction(data,error,isValidating){conststate=ref('idle')watchEffect(()=>{if(data.value&&isValidating.value){state.value=STATES.VALIDATINGreturn}if(data.value&&error.value){state.value=STATES.STALE_IF_ERRORreturn}if(data.value===undefined&&!error.value){state.value=STATES.PENDINGreturn}if(data.value&&!error.value){state.value=STATES.SUCCESSreturn}if(data.value===undefined&&error){state.value=STATES.ERRORreturn}})return{ state,STATES,}}
And then in your template you can use it like so:
<template> <div> <divv-if="[STATES.ERROR, STATES.STALE_IF_ERROR].includes(state)"> {{ error }} </div> <divv-if="[STATES.PENDING].includes(state)">Loading...</div> <divv-if="[STATES.VALIDATING].includes(state)"><!-- serve stale content without "loading"--> </div> <divv-if=" [STATES.SUCCESS, STATES.VALIDATING, STATES.STALE_IF_ERROR].includes( state )" > {{ data }} </div> </div></template><script>import {computed }from'vue'importuseSwrvStatefrom'@/composables/useSwrvState'importuseSWRVfrom'swrv'exportdefault { name:'Repo',setup(props, { root }) {constpage=computed(()=>root.$route.params.id)const {data,error,isValidating }=useSWRV( ()=>`/api/${root.$route.params.id}`, fetcher )const {state,STATES }=useSwrvState(data, error, isValidating)return { state,STATES, data, error, page, isValidating, } },}</script>
Most of the features of swrv handle the complex logic / ceremony that you'd have to implement yourself inside a vuex store. All swrv instances use the same global cache, so if you are using swrv alongside vuex, you can use global watchers on resolved swrv returned refs. It is encouraged to wrap useSWRV in a custom composable function so that you can do application level side effects if desired (e.g. dispatch a vuex action when data changes to log events or perform some logic).
Vue 3 example:
<script>import {defineComponent,ref,computed,watch }from'vue'import {useStore }from'vuex'importuseSWRVfrom'swrv'import {getAllTasks }from'./api'exportdefaultdefineComponent({setup() {conststore=useStore()consttasks=computed({get: ()=>store.getters.allTasks,set: (tasks)=> {store.dispatch('setTaskList', tasks) }, })constaddTasks= (newTasks)=>store.dispatch('addTasks', { tasks: newTasks })const {data }=useSWRV('tasks', getAllTasks)// Using a watcher, you can update the store with any changes coming from swrvwatch(data,newTasks=> {store.dispatch('addTasks', { source:'Todoist', tasks: newTasks }) })return { tasks } },})</script>
By default, a custom cache implementation is used to store fetcher response data cache, in-flight promise cache, and ref cache. Response data cache can be customized via theconfig.cache
property. Built in cache adapters:
A common usage case to have a betteroffline experience is to read fromlocalStorage
. Checkout thePWA example for more inspiration.
importuseSWRVfrom'swrv'importLocalStorageCachefrom'swrv/dist/cache/adapters/localStorage'functionuseTodos(){const{ data, error}=useSWRV('/todos',undefined,{cache:newLocalStorageCache('swrv'),shouldRetryOnError:false})return{ data, error}}
To only retrieve a swrv cache response without revalidating, you can set the fetcher function tonull
from the useSWRV call. This can be useful when there is some higher level swrv composable that is always sending data to other instances, so you can assume that composables with anull
fetcher will have data available. Thisisn't very intuitive, so will be looking for ways to improve this api in the future.
// Component Aconst{ data}=useSWRV('/api/config',fetcher)// Component B, only retrieve from cacheconst{ data}=useSWRV('/api/config',null)
Sinceerror
is returned as a Vue Ref, you can use watchers to handle any onError callback functionality. Check outthe test.
exportdefault{setup(){const{ data, error}=useSWRV(key,fetch)functionhandleError(error){console.error(error&&error.message)}watch(error,handleError)return{ data, error,}},}
How is swrv different from theswr react library
Theswrv
library is meant to be used with the Vue Composition API (and eventually Vue 3) so it utilizes Vue's reactivity system to track dependencies and returns vueRef
's as it's return values. This allows you to watchdata
or build your own computed props. For example, the key function is implemented as Vuewatch
er, so any changes to the dependencies in this function will trigger a revalidation inswrv
.
Features were built as needed forswrv
, and while the initial development ofswrv
was mostly a port of swr, the feature sets are not 1-1, and are subject to diverge as they already have.
The idea behind stale-while-revalidate is that you always get fresh data eventually. You can disable some of the eager fetching such asconfig.revalidateOnFocus
, but it is preferred to serve a fast response from cache while also revalidating so users are always getting the most up to date data.
Swrv fetcher functions can be triggered on-demand by using themutate
return value. This is useful when there is some event that needs to trigger a revalidation such a PATCH request that updates the initial GET request response data.
Thanks goes to these wonderful people (emoji key):
Darren Jennings 💻📖 | Sébastien Chopin 💻🤔 | Fernando Machuca 🎨 | ZEIT 🤔 | Adam DeHaven 💻📖🚧 |
This project follows theall-contributors specification. Contributions of any kind welcome!
About
Stale-while-revalidate data fetching for Vue