1

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>
kissu
  • 40,416
  • 14
  • 65
  • 133
user1818290
  • 91
  • 1
  • 1
  • 6
  • Hi, you should ask only one question at a time. For "How can I do multiple asyncData requests on a single page", isn't your code working fine here so far? (`asyncData`) – kissu May 04 '21 at 01:31
  • No, I get a runtime error. I see I have defined arrangers and composers twice. But if I take out either of the definitions (the let or the const), I don't get a runtime error, but I don't get any data either. – user1818290 May 04 '21 at 16:46
  • Also, the part of your question from `I also have the problem of getting...` should be removed. Try to ask only one thing per question post. If you want to ask the second part, simply create a new post. Otherwise, you may see your question closed for the `Needs more focus` reason (`This question currently includes multiple questions in one. It should focus on one problem only`). – kissu May 05 '21 at 01:27
  • I've edited your initial question to have it scoped to one thing only. Feel free to post the second part into another post! – kissu May 05 '21 at 09:06

1 Answers1

1

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.

kissu
  • 40,416
  • 14
  • 65
  • 133
  • Thanks for responding. I tried: const { data1: arrangers } = await context.$axios.get('/api/arrangers') const { data2: composers } = await context.$axios.get('/api/composers') console.log(arrangers, composers) return { arrangers, composers } Both arrangers and composers come back undefined. – user1818290 May 04 '21 at 23:23
  • Hm wait, I didn't noticed before but why are you using a `Promise.all` and an `$axios.get` just after? You should only have one of the two. On top of that, you need to have the renamed variable name on the right and not on the left, hence `data1` is what the API is returning to you in this case, so probably not the case. Are the APIs public for me to try out? Or can you share some network payloads? – kissu May 05 '21 at 00:07
  • Thank you for the in-depth reply!!! This worked for my 2 starter requests. Now to add the other 7 requests. I realize I have a lot to learn. I did notice the duplication with the Promise and the $axios.get, but removing either one separately made no difference. Your code worked. – user1818290 May 05 '21 at 02:11