1

I am chaining a bunch of axios get request to different endpoints of an API and I'm trying to create an array like this from the data (simplified):

[
    {
        name: "John",
        id: 1,
        gender: "male"
    },
    {
        name: "Anna",
        id: 2,
        gender: "female"
    },
]

In one of the requests I retrieve each person's name and id in an array like this:

[
    {
        name: "John",
        id: 1
    },
    {
        name: "Anna",
        id: 2
    },
]

Now I only need to get their gender by sending each persons's id in two separate requests to an endpoint.

I have spent hours trying to construct the array at the top with push() and then() but I just can't get it right.

How do I go about this?

I'm chaining the axios requests like this btw:

axios.get('api/' + endpoint1]).then(response => {
    axios.get('api/' + endpoint2).then(response => {
        axios.get('api/' + endpoint3).then(response => {
            // and so on...
         });
    });
});

UPDATE 1:

I feel like I didn't explain the problem properly. This is what my code looks like right now, starting from the last promise. How can I change it in order to get the array at the top of my question?

.then(response => {

    people= response.data; // Array of people including their name id (not their gender though)

    for (var key in people) {

        var person = {};

        person["name"] = people[key].node.name;

        person["id"] = people[key].node.id;

        finalArray.push(person);

        axios.get('api/' + endpoint3, { // Endpoint for getting a persons gender from id
            params: {
                personId: person.id
                }
        }).then(response => {

            // I don't know what to do here...            

        });

    }

    console.log(finalArray); // Gives me an array of objects without "gender".

});

UPDATE 2:

Thanks alot for your answers!

I've combined some of your solutions and this is how my real code looks right now. The requests to http://api.ntjp.se/coop/api/v1/serviceProducers.json are not sent. Why?

I also don't want to keep the whole objects within the cooperations response array before calling http://api.ntjp.se/coop/api/v1/serviceProducers.json. I just want to save two specific key/value pairs from each object. These two key/value pairs are both within an object called "serviceContract" within in each response object. How do I save them?

<html>

<head>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>

<body>

    <script>

        getConnectionStatusData();

        async function getConnectionStatusData() {

            let serviceDomains = await axios.get('http://api.ntjp.se/coop/api/v1/serviceDomains.json', {
                                        params: {
                                            namespace: "crm:scheduling"
                                        }
                                    });

            serviceDomainId = serviceDomains.data[0].id;

            let connectionPoints = await axios.get('http://api.ntjp.se/coop/api/v1/connectionPoints.json', {
                                        params: {
                                            platform: "NTJP",
                                            environment: "PROD"
                                        }
                                    });

            connectionPointId = connectionPoints.data[0].id;

            let logicalAddresss = await axios.get('http://api.ntjp.se/coop/api/v1/logicalAddresss.json', {
                                        params: {
                                            logicalAdress: "SE2321000016-167N",
                                            serviceConsumerHSAId: "SE2321000016-92V4",
                                            connectionPointId: connectionPointId
                                        }
                                    });

            logicalAddressId = logicalAddresss.data[0].id;

            let serviceConsumers = await axios.get('http://api.ntjp.se/coop/api/v1/serviceConsumers.json', {
                                        params: {
                                            connectionPointId: connectionPointId,
                                            logicalAddressId: logicalAddressId
                                        }
                                    });

            consumer = serviceConsumers.data.filter(obj => {
                  return obj.hsaId === "SE2321000016-92V4"
                });

            serviceConsumerId = consumer[0].id;

            let cooperations = await axios.get('http://api.ntjp.se/coop/api/v1/cooperations.json', {
                                        params: {
                                            connectionPointId: connectionPointId,
                                            logicalAddressId: logicalAddressId,
                                            serviceDomainId: serviceDomainId,
                                            serviceConsumerId: serviceConsumerId,
                                            include: "serviceContract"
                                        }
                                    });

            for(var idx in cooperations.data) {

                var data = async () => { return await axios.get('http://api.ntjp.se/coop/api/v1/serviceProducers.json', {
                                        params: {
                                            connectionPointId: connectionPointId,
                                            logicalAddressId: logicalAddressId,
                                            serviceDomainId: serviceDomainId,
                                            serviceConsumerId: serviceConsumerId,
                                            serviceContractId: cooperations.data[idx].serviceContract.id
                                        }
                                    }) }
                cooperations.data[idx].producerDescription = data.description;
                cooperations.data[idx].producerHSAId = data.hsaId;
            }

            console.log(cooperations.data);

        }

    </script>

</body>

UPDATE 3

I finally made it work but why do I have to reference to the data like response.data[0].description when I push it into finalResult in the end? I mean, why doesn't just response.data.description work, as it does for @Cold Cerberus?

Other than that, is my code ok in or have I done something wrong?

Thanks guys!

<html>

<head>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>

<body>

    <script>

        getConnectionStatusData();

        async function getConnectionStatusData() {

            let serviceDomains = await axios.get('http://api.ntjp.se/coop/api/v1/serviceDomains.json', {
                                        params: {
                                            namespace: "crm:scheduling"
                                        }
                                    });

            serviceDomainId = serviceDomains.data[0].id;

            let connectionPoints = await axios.get('http://api.ntjp.se/coop/api/v1/connectionPoints.json', {
                                        params: {
                                            platform: "NTJP",
                                            environment: "PROD"
                                        }
                                    });

            connectionPointId = connectionPoints.data[0].id;

            let logicalAddresss = await axios.get('http://api.ntjp.se/coop/api/v1/logicalAddresss.json', {
                                        params: {
                                            logicalAdress: "SE2321000016-167N",
                                            serviceConsumerHSAId: "SE2321000016-92V4",
                                            connectionPointId: connectionPointId
                                        }
                                    });

            logicalAddressId = logicalAddresss.data[0].id;

            let serviceConsumers = await axios.get('http://api.ntjp.se/coop/api/v1/serviceConsumers.json', {
                                        params: {
                                            connectionPointId: connectionPointId,
                                            logicalAddressId: logicalAddressId
                                        }
                                    });

            consumer = serviceConsumers.data.filter(obj => {
                  return obj.hsaId === "SE2321000016-92V4"
                });

            serviceConsumerId = consumer[0].id;

            let cooperations = await axios.get('http://api.ntjp.se/coop/api/v1/cooperations.json', {
                                        params: {
                                            connectionPointId: connectionPointId,
                                            logicalAddressId: logicalAddressId,
                                            serviceDomainId: serviceDomainId,
                                            serviceConsumerId: serviceConsumerId,
                                            include: "serviceContract"
                                        }
                                    });

            var finalData = [];

            cooperations.data.forEach(function(cooperation) {

                axios.get('http://api.ntjp.se/coop/api/v1/serviceProducers.json', {
                            params: {
                                connectionPointId: connectionPointId,
                                logicalAddressId: logicalAddressId,
                                serviceDomainId: serviceDomainId,
                                serviceConsumerId: serviceConsumerId,
                                serviceContractId: cooperation.serviceContract.id
                            }
                }).then(response => {
                finalData.push({serviceContract: cooperation.serviceContract.namespace, serviceProducerDescription: response.data[0].description, serviceProducerHSAId: response.data[0].hsaId});
            });


            });

            console.log(finalData);



        }

    </script>

</body>

Rawland Hustle
  • 781
  • 1
  • 10
  • 16

3 Answers3

1

I'm not quite sure of your specific problem. But assuming that what you mean is you have two endpoints, first is the one that returns an array of object (lets call it 'getPeopleArray'):

[
 {
    name: "John",
    id: 1
 },
 {
    name: "Anna",
    id: 2
 }
]

and second endpoint returns the gender of the given id (lets call it 'getGender' with one param id), .push will not do the job for you.

Your problem can be solved with something like this:

let peopleArray = [];
axios.get('api/' + 'getPeopleArray').then(people => {
    peopleArray = people;
    people.forEach((person, index) => {
       axios.get('api/' + 'getGender?id='.concat(person.id.toString()))
           .then(gender => {
               peopleArray[index].gender = gender;
           });
    });
});

First is you save the returned array of your first request and then you will have to loop through each object in that array to get and assign their genders from your second endpoint using the index argument of your [].forEach(callbackfn). As long as there is no manipulation with peopleArray during or before all requests are finished, the index will be correct.

Update 2: In response to your question in the comments "why .push doesn't work?", I decided to make another approach If you want to end your algorithm with using .push and go without tracking indexes.

let peopleArray = [];
axios.get('api/' + 'getPeopleArray').then(people => {
    people.forEach((person) => {
       axios.get('api/' + 'getGender?id='.concat(person.id.toString()))
           .then(gender => {
               peopleArray.push({id: person.id, name: person.name, gender, gender});
           });
    });
});

This way you will only push your object to your collection peopleArray when its respective gender is also fetched. This will also eliminate having to use .map (as suggested in the comments) for storing only the individual object's properties you desire to keep since you pushed a new structured object on line peopleArray.push({id: person.id, name: person.name, gender, gender});.

Alex Pappas
  • 2,377
  • 3
  • 24
  • 48
  • Thanks! What's the reason why .push doesn't work? Also, the response from getPeopleArray includes a bunch of data that I don't want to save. I just want to save name and id. Will it still work? – Rawland Hustle Feb 18 '19 at 14:03
  • Thanks! Please look at UPDATE 2 of my original post. – Rawland Hustle Feb 18 '19 at 15:16
  • @RawlandHustle Yes you can actually just save the specific properties per object in the array by using `.map`. So instead of just having to save `people` in the `peopleArray` (looking at the given example `peopleArray = people;`), you'll have to do it with .map. Example: `peopleArray = people.map(p => { id: p.id, name: p.name});` – Alex Pappas Feb 19 '19 at 02:49
  • @RawlandHustle, the `.push` does what it's supposed to do which is just add an element to the array. And in your case you don't want to _push_ the genders to your array of _id-name-object_'s. You'll get something like `[{id: 1, name:'abc'}, {id: 2, name:'def'}, 'female']` instead of adding a `gender` property to its corresponding _id-name-object_ from the array. – Alex Pappas Feb 19 '19 at 06:58
  • @RawlandHustle added Update 2 to my answer too . Please see – Alex Pappas Feb 19 '19 at 07:07
  • Thank you very much! I need to put "function" in the forEach loop, otherwise I get an error. Why? Eitherway, I can't get it to work. The challenge is that the data I want from the response that you call "people", is within a child of each object. Like in people.info.name and people.info.id. But if I try to reference the data like people.info.name in the loop I get 'undefinied'. – Rawland Hustle Feb 19 '19 at 16:37
  • See Update 3 instead. – Rawland Hustle Feb 19 '19 at 16:59
  • @RawlandHustle, with regards to the `data[0]` part . It's really up to your endpoint how it structured the data to return. In that case, that's the object it returned. just `console.log` the data returned so that you can see what the whole object returned by the endpoint looks like. With regards to the `foreach` loop, it does require a `callbackfn` as a parameter that is why it says you need a "function" in it you can do it with classic `array.forEach(function(el){ //do something here})` or with es6's arrow function syntax `array.forEach((el)=>{// do something here})` which is what I used. – Alex Pappas Feb 20 '19 at 02:15
  • Cerburs: Thanks! For some reason the array in my object shows up empty when send it or when I try to JSON.stringify it. I created a separate question: https://stackoverflow.com/questions/54775668/array-in-object-empty-when-passed Can you PLEASE have a look at it? – Rawland Hustle Feb 20 '19 at 02:24
1

I do not like to read chained promises and prefer to use async/await instead. You could get your list first and then loop through that list with a map and request the gender for each name. Remember that you have to wait for all promises to resolve inside your map before you can proceed.

const axios = require('axios');

async function getPeople() {
    let firstResult = await axios.get('api/path/endpoint1');
    // firstResult = [{name: "John", id: 1}, {name: "Anna", id: 2}]

    let updatedResult = firstResult.map(async item => {
        let people = await axios.get('api/path/endpoint2' + item.name); // or however your endpoint is designed 
        // people = {name: "John", id: 1, gender: male}
        item.gender = people.gender;
        return item; 
    });
    // updatedResult = undefined

    Promise.all(updatedResult)
        .then(finalResult => console.log(finalResult));
    //  [{name: "John", id: 1, gender: male}, {name: "Anna", id: 2, gender: female}]
}
  • Wow! I'll try that. But after each call I have to run some filter functions etc. on the retrieved data before performing the next call. Like this: var John = response.data.filter(obj => { return obj.someRetrivedValue === someStoredValue }) How do I implement it? – Rawland Hustle Feb 18 '19 at 08:39
  • You can do whatever you want after each call before you proceed with the next step. So you can simply add your line in between, no problem :) – Alberti Buonarroti Feb 18 '19 at 08:52
  • Why is Promise.all needed? Doesn't await mean to wait before going on to the next call? And if I have 10 calls, do I need to put all their names (let's) in Promise.all()? – Rawland Hustle Feb 18 '19 at 09:26
  • If you `await` inside a `map` it will return promises which need to be resolved. You put the names inside the map function. So you create an array of names=["Alex", "Anna"] and use this array to call map names.map(//async function here). After done you use names as an argument like this: `Promise.all(names).then(result => console.log(result));` – Alberti Buonarroti Feb 18 '19 at 09:28
  • Okay, so here's how far I got with my *real* data: https://jsfiddle.net/y4fgqhkx/1/ The fiddle can't be run since the API is on http, but you can atleast see the code. Note the comment at the end. That's where I'm stuck. Can you PLEASE help me? – Rawland Hustle Feb 18 '19 at 10:09
  • I don't think I can use map, since I recieve "id" (which is btw) but then sending it as "serviceContractId". Map won't work then, right? – Rawland Hustle Feb 18 '19 at 10:17
  • Are `connectionPointId`, `logicalAddressId`, `serviceContractId` and `serviceConsumerId` arrays? And do they all have the same length? – Alberti Buonarroti Feb 18 '19 at 10:49
  • They are all integers of various lenghts. – Rawland Hustle Feb 18 '19 at 12:17
  • Please look at UPDATE 2 of my original post. – Rawland Hustle Feb 18 '19 at 15:15
0

You can use async/awaits and reassign gender key to first endpoint data ...

var users;
axios.get('api/' + endpoint1]).then(response => {
   users = response; // assume all user id list
   for(var idx in users) {
     var data = async () => { return await axios.get('api/' + users[idx].id) } //get gender api by user id
     users[idx].gender = data.gender; //create gender key value 
   }
   console.log(users);
});