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 when fetch is being called on client-side.
  • $fetchState.error: null or Error, allows you to display an error message
  • $fetchState.timestamp: Integer, is a timestamp of the last fetch, useful for caching with keep-alive

If you want to call the fetch hook from your component methods or template use:

  1. <button @click="$fetch">Refresh</button>

You can access the Nuxt context within the fetch hook using this.$nuxt.context.

Options

  • fetchOnServer: Boolean (default: true), call fetch() when server-rendering the page
  • fetchDelay: 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.

  1. <script>
  2. export default {
  3. data () {
  4. return {
  5. posts: []
  6. }
  7. },
  8. async fetch () {
  9. this.posts = await this.$http.$get('https://jsonplaceholder.typicode.com/posts')
  10. },
  11. fetchOnServer: false
  12. }
  13. </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

  1. <template>
  2. <div>
  3. <h1>Blog posts</h1>
  4. <p v-if="$fetchState.pending">Fetching posts...</p>
  5. <p v-else-if="$fetchState.error">Error while fetching posts: {{ $fetchState.error.message }}</p>
  6. <ul v-else>
  7. <li v-for="post of posts" :key="post.id">
  8. <n-link :to="`/posts/${post.id}`">{{ post.title }}</n-link>
  9. </li>
  10. </ul>
  11. </div>
  12. </template>
  13. <script>
  14. export default {
  15. data () {
  16. return {
  17. posts: []
  18. }
  19. },
  20. async fetch () {
  21. this.posts = await this.$http.$get('https://jsonplaceholder.typicode.com/posts')
  22. }
  23. }
  24. </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). Screenshot 2019-03-11 at 23 04 57

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

  1. <template>
  2. <div v-if="$fetchState.pending">Fetching post #{{$route.params.id}}...</div>
  3. <div v-else>
  4. <h1>{{ post.title }}</h1>
  5. <pre>{{ post.body }}</pre>
  6. <p><n-link to="/">Back to posts</n-link></p>
  7. </div>
  8. </template>
  9. <script>
  10. export default {
  11. data () {
  12. return {
  13. post: {}
  14. }
  15. },
  16. async fetch() {
  17. this.post = await this.$http.$get(`https://jsonplaceholder.typicode.com/posts/${this.$route.params.id}`)
  18. }
  19. }
  20. </script>

When navigating, you should now see "Loading post #…" on client-side, and no loading when refreshing a post (hard refresh on the browser). fetch-nuxt3

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:

  1. export default {
  2. watch: {
  3. '$route.query': '$fetch'
  4. },
  5. async fetch() {
  6. // Called also on query changes
  7. }
  8. }

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

  1. <template>
  2. <nuxt keep-alive />
  3. </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

  1. <template>
  2. ...
  3. </template>
  4. <script>
  5. export default {
  6. data () {
  7. return {
  8. post: {}
  9. }
  10. },
  11. activated() {
  12. // Call fetch again if last fetch more than 30 sec ago
  13. if (this.$fetchState.timestamp <= (Date.now() - 30000)) {
  14. this.$fetch()
  15. }
  16. },
  17. async fetch() {
  18. this.post = await this.$http.$get(`https://jsonplaceholder.typicode.com/posts/${this.$route.params.id}`)
  19. }
  20. }
  21. </script>

The navigation to the same page will not call fetch if last fetch call was before 30 sec ago.

fetch-keep-alive-nuxt

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:

  1. <template>
  2. <h1>Stars: {{ $store.state.stars }}</h1>
  3. </template>
  4. <script>
  5. export default {
  6. fetch ({ store, params }) {
  7. return axios.get('http://my-api/stars')
  8. .then((res) => {
  9. store.commit('setStars', res.data)
  10. })
  11. }
  12. }
  13. </script>

You can also use async/await to make your code cleaner:

  1. <template>
  2. <h1>Stars: {{ $store.state.stars }}</h1>
  3. </template>
  4. <script>
  5. export default {
  6. async fetch ({ store, params }) {
  7. let { data } = await axios.get('http://my-api/stars')
  8. store.commit('setStars', data)
  9. }
  10. }
  11. </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:

  1. <script>
  2. export default {
  3. async fetch ({ store, params }) {
  4. await store.dispatch('GET_STARS');
  5. }
  6. }
  7. </script>

store/index.js

  1. // ...
  2. export const actions = {
  3. async GET_STARS ({ commit }) {
  4. const { data } = await axios.get('http://my-api/stars')
  5. commit('SET_STARS', data)
  6. }
  7. }

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.