API: The fetch Method
Nuxt >= 2.12
Nuxt.js v2.12
introduces a new hook called fetch
in any of your Vue components.
See live demo and code example.
fetch(context)
has been deprecated, instead you can use an anonymous middleware in your page: middleware(context)
When to use fetch?
Every time you need to get asynchronous data. fetch
is called on server-side when rendering the route, and on client-side when navigating.
It exposes $fetchState
at the component level:
$fetchState.pending
:Boolean
, allows you to display a placeholder whenfetch
is being called on client-side.$fetchState.error
:null
orError
, allows you to display an error message$fetchState.timestamp
:Integer
, is a timestamp of the last fetch, useful for caching withkeep-alive
If you want to call the fetch
hook from your component methods or template use:
<button @click="$fetch">Refresh</button>
You can access the Nuxt context within the fetch hook using this.$nuxt.context
.
Options
fetchOnServer
:Boolean
(default:true
), callfetch()
when server-rendering the pagefetchDelay
:Integer
(default:200
), set the minimum executing time in milliseconds (to avoid quick flashes)
When fetchOnServer
is false
, fetch
will be called only on client-side and $fetchState.pending
will return true
when server-rendering the component.
<script>
export default {
data () {
return {
posts: []
}
},
async fetch () {
this.posts = await this.$http.$get('https://jsonplaceholder.typicode.com/posts')
},
fetchOnServer: false
}
</script>
Example
We are going to use the official http module to make HTTP requests.
Let's have a blog with our home page listing our posts:
pages/index.vue
<template>
<div>
<h1>Blog posts</h1>
<p v-if="$fetchState.pending">Fetching posts...</p>
<p v-else-if="$fetchState.error">Error while fetching posts: {{ $fetchState.error.message }}</p>
<ul v-else>
<li v-for="post of posts" :key="post.id">
<n-link :to="`/posts/${post.id}`">{{ post.title }}</n-link>
</li>
</ul>
</div>
</template>
<script>
export default {
data () {
return {
posts: []
}
},
async fetch () {
this.posts = await this.$http.$get('https://jsonplaceholder.typicode.com/posts')
}
}
</script>
If you go directly to http://localhost:3000/, you will see directly the full list of posts which has been server-rendered (great for SEO).
Nuxt will intelligently detect what data you mutated inside fetch
and will optimise the JSON included in the returned HTML.
Now, let's add pages/posts/_id.vue
page to display a post on /posts/:id
.
pages/posts/_id.vue
<template>
<div v-if="$fetchState.pending">Fetching post #{{$route.params.id}}...</div>
<div v-else>
<h1>{{ post.title }}</h1>
<pre>{{ post.body }}</pre>
<p><n-link to="/">Back to posts</n-link></p>
</div>
</template>
<script>
export default {
data () {
return {
post: {}
}
},
async fetch() {
this.post = await this.$http.$get(`https://jsonplaceholder.typicode.com/posts/${this.$route.params.id}`)
}
}
</script>
When navigating, you should now see "Loading post #…"
on client-side, and no loading when refreshing a post (hard refresh on the browser).
If the component contains the fetch
hook, you will also have access to this.$fetch()
to re-call the fetch
hook ($fetchState.pending
will become true
again).
Listening to query string changes
The fetch
hook is not called on query string changes by default. To watch for query changes you can add a watcher on $route.query
and call $fetch
:
export default {
watch: {
'$route.query': '$fetch'
},
async fetch() {
// Called also on query changes
}
}
Caching
You can use keep-alive
directive in <nuxt/>
and <nuxt-child/>
component to save fetch
calls on pages you already visited:
layouts/default.vue
<template>
<nuxt keep-alive />
</template>
You can also specify the props passed to <keep-alive>
by passing a prop keep-alive-props
to the <nuxt>
component.Example: <nuxt keep-alive :keep-alive-props="{ max: 10 }" />
to keep only 10 page components in memory.
Using activated hook
Nuxt will directly fill this.$fetchState.timestamp
(timestamp) of the last fetch
call (ssr included). You can use this property combined with activated
hook to add a 30 seconds cache to fetch
:
pages/posts/_id.vue
<template>
...
</template>
<script>
export default {
data () {
return {
post: {}
}
},
activated() {
// Call fetch again if last fetch more than 30 sec ago
if (this.$fetchState.timestamp <= (Date.now() - 30000)) {
this.$fetch()
}
},
async fetch() {
this.post = await this.$http.$get(`https://jsonplaceholder.typicode.com/posts/${this.$route.params.id}`)
}
}
</script>
The navigation to the same page will not call fetch
if last fetch
call was before 30 sec ago.
Nuxt <= 2.11
The fetch method is used to fill the store before rendering the page, it's like the
asyncData
method except it doesn't set the component data.
- Type:
Function
The fetch
method, if set, is called every time before loading the component (only for page components). It will be called server-side once (on the first request to the Nuxt app) and client-side when navigating to further routes.
The fetch
method receives the context
object as the first argument, we can use it to fetch some data and fill the store. To make the fetch
method asynchronous, return a Promise, Nuxt.js will wait for the promise to be resolved before rendering the component.
Warning: You don't have access of the component instance through this
inside fetch
because it is called before initiating the component.
Example of pages/index.vue
:
<template>
<h1>Stars: {{ $store.state.stars }}</h1>
</template>
<script>
export default {
fetch ({ store, params }) {
return axios.get('http://my-api/stars')
.then((res) => {
store.commit('setStars', res.data)
})
}
}
</script>
You can also use async
/await
to make your code cleaner:
<template>
<h1>Stars: {{ $store.state.stars }}</h1>
</template>
<script>
export default {
async fetch ({ store, params }) {
let { data } = await axios.get('http://my-api/stars')
store.commit('setStars', data)
}
}
</script>
Vuex
If you want to call a store action, use store.dispatch
inside fetch
, make sure to wait for the end of the action by using async
/await
inside:
<script>
export default {
async fetch ({ store, params }) {
await store.dispatch('GET_STARS');
}
}
</script>
store/index.js
// ...
export const actions = {
async GET_STARS ({ commit }) {
const { data } = await axios.get('http://my-api/stars')
commit('SET_STARS', data)
}
}
Listening to query string changes
The fetch
method is not called on query string changes by default. If you want to change this behavior, for example when building a pagination component, you can setup parameters that should be listened to through the watchQuery
property of your page component. Learn more on the API watchQuery
page.