swrv
(pronounced "swerve") is a library using the
@vue/composition-api for remote data
fetching. It is largely a port of swr.
The name “SWR” is derived from stale-while-revalidate, a cache invalidation strategy popularized by HTTP RFC 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
- Revalidation on focus
- Interval polling
- Request deduplication
- TypeScript ready
- Minimal API
- stale-if-error
- Customizable cache implementation
- Error Retry
- SSR support
- Vue 3 Support
With swrv
, 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 ✨
# npm
npm install swrv
# yarn
yarn add swrv
# pnpm
pnpm add swrv
If you want to try out Vue 3 support (beta), install the beta release and
check out the Vite example.
swrv
code for Vue 3.0 exists on next
branch.
# npm
npm install swrv@beta
# yarn
yarn add swrv@beta
# pnpm
pnpm add swrv@beta
<template>
<div>
<div v-if="error">failed to load</div>
<div v-if="!data">loading...</div>
<div v-else>hello {{ data.name }}</div>
</div>
</template>
<script>
import useSWRV from 'swrv'
export default {
name: 'Profile',
setup() {
const { data, error } = useSWRV('/api/user', fetcher)
return {
data,
error,
}
},
}
</script>
In this example, useSWRV
accepts a key
and a fetcher
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
and error
. When the request (fetcher)
is not yet finished, data will be undefined
. And when we get a response, it
sets data
and error
based on the result of fetcher and rerenders the
component. This is because data
and error
are Vue
Refs, 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. If ommitted, swrv uses the Fetch 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. If null , swrv will fetch from cache only and not revalidate. If omitted (i.e. undefined ) then the fetch api will be used. |
|
options |
an object of configuration options |
data
: data for the given key resolved by fetcher (or undefined if not loaded)error
: error thrown by fetcher (or undefined)isValidating
: if there's a request or revalidation loadingmutate
: function to trigger the validation manually
See Config Defaults
refreshInterval = 0
- polling interval in milliseconds. 0 means this is disabled.dedupingInterval = 2000
- dedupe requests with the same key in this time spanttl = 0
- time to live of response data in cache. 0 mean it stays around forever.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. Useful for when a component is serving from the cache immediately, but then un-mounts soon thereafter (e.g. a user clicking "next" in pagination quickly) to avoid unnecessary fetches.cache
- caching instance to store response data in. See src/lib/cache, and Cache below.
Prefetching can be useful for when you anticipate user actions, like hovering
over a link. SWRV exposes the mutate
function so that results can be stored in
the SWRV cache at a predetermined time.
import { mutate } from 'swrv'
function prefetch() {
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>
<p v-if="!projects">loading...</p>
<p v-else>You have {{ projects.length }} projects</p>
</template>
<script>
import { ref } from '@vue/composition-api'
import useSWRV from 'swrv'
export default {
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 a
stale-if-error strategy and
will maintain data
in the cache even if a useSWRV
fetch returns an error
.
<template>
<div v-if="error">failed to load</div>
<div v-if="data === undefined && !error">loading...</div>
<p v-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/composition-api'
import useSWRV from 'swrv'
export default {
name: 'Profile',
setup() {
const endpoint = 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 composable
useSwrvState
function:
import { ref, watchEffect } from '@vue/composition-api'
const STATES = {
VALIDATING: 'VALIDATING',
PENDING: 'PENDING',
SUCCESS: 'SUCCESS',
ERROR: 'ERROR',
STALE_IF_ERROR: 'STALE_IF_ERROR',
}
export default function(data, error, isValidating) {
const state = ref('idle')
watchEffect(() => {
if (data.value && isValidating.value) {
state.value = STATES.VALIDATING
return
}
if (data.value && error.value) {
state.value = STATES.STALE_IF_ERROR
return
}
if (data.value === undefined && !error.value) {
state.value = STATES.PENDING
return
}
if (data.value && !error.value) {
state.value = STATES.SUCCESS
return
}
if (data.value === undefined && error) {
state.value = STATES.ERROR
return
}
})
return {
state,
STATES,
}
}
And then in your template you can use it like so:
<template>
<div>
<div v-if="[STATES.ERROR, STATES.STALE_IF_ERROR].includes(state)">
{{ error }}
</div>
<div v-if="[STATES.PENDING].includes(state)">Loading...</div>
<div v-if="[STATES.VALIDATING].includes(state)">
<!-- serve stale content without "loading" -->
</div>
<div
v-if="
[STATES.SUCCESS, STATES.VALIDATING, STATES.STALE_IF_ERROR].includes(
state
)
"
>
{{ data }}
</div>
</div>
</template>
<script>
import { computed } from '@vue/composition-api'
import useSwrvState from '@/composables/useSwrvState'
import useSWRV from 'swrv'
export default {
name: 'Repo',
setup(props, { root }) {
const page = 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'
import useSWRV from 'swrv'
import { getAllTasks } from './api'
export default defineComponent({
setup() {
const store = useStore()
const tasks = computed({
get: () => store.getters.allTasks,
set: (tasks) => {
store.dispatch('setTaskList', tasks)
},
})
const addTasks = (newTasks) => store.dispatch('addTasks', { tasks: newTasks })
const { data } = useSWRV('tasks', getAllTasks)
// Using a watcher, you can update the store with any changes coming from swrv
watch(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 the config.cache
property. Built in cache adapters:
A common usage case to have a better offline experience is to read from
localStorage
. Checkout the PWA example for more inspiration.
import useSWRV from 'swrv'
import LocalStorageCache from 'swrv/dist/cache/adapters/localStorage'
function useTodos () {
const { data, error } = useSWRV('/todos', undefined, {
cache: new LocalStorageCache(),
shouldRetryOnError: false
})
return {
data,
error
}
}
To only retrieve a swrv cache response without revalidating, you can set the fetcher function to null
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 a null
fetcher will have data available. This
isn't very intuitive, so will be looking for ways to improve this api in the
future.
// Component A
const { data } = useSWRV('/api/config', fetcher)
// Component B, only retrieve from cache
const { data } = useSWRV('/api/config', null)
Since error
is returned as a Vue Ref, you can use watchers to handle any
onError callback functionality. Check out
the test.
export default {
setup() {
const { data, error } = useSWRV(key, fetch)
function handleError(error) {
console.error(error && error.message)
}
watch(error, handleError)
return {
data,
error,
}
},
}
How is swrv different from the swr react library
The swrv
library is meant to be used with the @vue/composition-api (and
eventually Vue 3) library so it utilizes Vue's reactivity system to track
dependencies and returns vue Ref
's as it's return values. This allows you to
watch data
or build your own computed props. For example, the key function is
implemented as Vue watch
er, so any changes to the dependencies in this
function will trigger a revalidation in swrv
.
Features were built as needed for swrv
, and while the initial development of
swrv
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 as
config.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 the mutate
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 🤔 |
Jason Yang/楊朝傑 🐛 💻 |
Axel Hernández Ferrera 🐛 💻 💡 |
This project follows the all-contributors specification. Contributions of any kind welcome!