How to array destructure a Promise.all in Nuxt's asyncData

1.7k views Asked by At

I am working with Nuxt and Vue, with MySQL database, all of which are new to me. I am transitioning out of WebMatrix, where I had a single Admin page for multiple tables, with dropdowns for selecting a particular option. On this page, I could elect to add, edit or delete the selected option, say a composer or music piece. Here is some code for just 2 of the tables (gets a runtime error of module build failed):

<script> 
export default {
  async asyncData(context) {
    let [{arrangers}, {composers}] = await Promise.all([
      context.$axios.get(`/api/arrangers`),
      context.$axios.get(`/api/composers`),
    ])
    const {arrangers} = await context.$axios.get('/api/arrangers')
    const {composers} = await context.$axios.get('/api/composers')  

    return { arrangers, composers } 
  },
}
</script>
1

There are 1 answers

3
kissu On BEST ANSWER

You do have the same variable name for both the input (left part of Promise.all) and as the result from your axios call, to avoid naming collision, you can rename the result and return this:

const { arrangers: fetchedArrangers } = await context.$axios.get('/api/arrangers')
const { composers: fetchedComposers } = await context.$axios.get('/api/composers')

return { fetchedArrangers, fetchedComposers } 

EDIT, this is how I'd write it

async asyncData({ $axios }) {
  const [posts, comments] = await Promise.all([
    $axios.$get('https://jsonplaceholder.typicode.com/posts'),
    $axios.$get('https://jsonplaceholder.typicode.com/comments'),
  ])
  console.log('posts', posts)
  console.log('comments', comments)

  return { posts, comments }
},

When you destructure at the end of the result of a Promise.all, you need to destructure depending of the result that you'll get from the API. Usually, you do have data, so { arrangers } or { composers } will usually not work. Of course, it depends of your own API and if you return this type of data.

Since destructuring 2 data is not doable, it's better to simply use array destructuring. This way, it will return the object with a data array inside of it.

To directly have access to the data, you can use the $get shortcut, which comes handy in our case. Directly destructuring $axios is a nice to have too, will remove the dispensable context.

In my example, I've used JSONplaceholder to have a classic API behavior (especially the data part) but it can work like this with any API.

Here is the end result.

enter image description here

Also, this is what happens if you simply use this.$axios.get: you will have the famous data that you will need to access to later on (.data) at some point to only use the useful part of the API's response. That's why I do love the $get shortcut, goes to the point faster.

enter image description here


PS: all of this is possible because Promise.all preserve the order of the calls: https://stackoverflow.com/a/28066851/8816585


EDIT2: an example on how to make it more flexible could be

async asyncData({ $axios }) {
  const urlEndpointsToFetchFrom = ['comments', 'photos', 'albums', 'todos', 'posts']
  const allResponses = await Promise.all(
    urlEndpointsToFetchFrom.map((url) => $axios.$get(`https://jsonplaceholder.typicode.com/${url}`)),
  )

  const [comments, photos, albums, todos, posts] = allResponses
  return { comments, photos, albums, todos, posts }
},

Of course, preserving the order in the array destructuring is important. It's maybe doable in a dynamic way but I don't know how tbh.

Also, I cannot recommend enough to also try the fetch() hook alternative someday. I found it more flexible and it does have a nice $fetchState.pending helper, more here: https://nuxtjs.org/blog/understanding-how-fetch-works-in-nuxt-2-12/ and in the article on the bottom of the page.