Reputation: 5927
I am trying to call the API in sequential order and I will use the previous response for the next API call. And I will be having the N number of API calls, Lets consider below is my sample array I have,
data = [
{
api: '/api/allGeneTables/',
},
{
api: '/api/tables/',
fieldName: 'profiles', // I am using this for building api
concatWithPrevField: true,
prevField: 'positions' // I am using this for building api
},
{
api: '/api/tables/superpositions',
fieldName: 'identical', // I am using this for building api
concatWithPrevField: true,
prevField: 'length' // I am using this for building api
}
]
First API will produce the data like this
https://myurl/api/allGeneTables/
API Response:
{
'positions': 10,
'index': 12,
'name': 'triosephaspate'
}
I need to create the next api with the previous value (positions) along with given fieldName (profiles), so the the second api would be
https://myurl/api/tables/10/profiles
// 10 came from the previous response value (positions)
// profiles is one of field's value of the current array
API Response:
{
'lipidprofile': 15,
'maxvalue': 12,
'name': 'triosephaspate',
'length':232
}
similar way I need to create the next API.
https://myurl/api/tables/superpositions/232/identical
// 232 came from the previous response value (length)
// identical is one of field's value of the current array
I have tried with the map operator and following is my code
from(data).pipe(map(res => {
const eachObj: any = res;
let urlConcat = '';
console.log(res);
if (eachObj.concatWithPrevField) {
// urlConcat = eachObj.api + ;
} else {
urlConcat = eachObj.api;
}
return this.dhs.getGeneData(urlConcat).subscribe();
})).subscribe();
I stuck at, how to join the previous API response value with the current data to make the API URL so I can call the next API. I am trying this with the RXJS methods.
Upvotes: 0
Views: 1815
Reputation: 5144
last_result
).concatMap
to wait for the API calls being completed before the next one is initiated and use the new value in last_result
.reduce
to complete the observable stream and emit the latest API-response (last_result
).Stackblitz: https://stackblitz.com/edit/rxjs-v5pg4n?file=index.ts
import { from, interval } from "rxjs";
import { concatMap, map, reduce, take } from "rxjs/operators";
const data_source = [
{
api: "/api/allGeneTables/"
},
{
api: "/api/tables/",
fieldName: "profiles", // I am using this for building api
concatWithPrevField: true,
prevField: "positions" // I am using this for building api
},
{
api: "/api/tables/superpositions/",
fieldName: "identical", // I am using this for building api
concatWithPrevField: true,
prevField: "length" // I am using this for building api
}
];
call_sequence(data_source).subscribe(console.log);
function call_sequence(data) {
let last_result = null;
return from(data).pipe(
// concatmap awaits finishing of request ( last_result is being set and emits the API response)
concatMap(fake_fetch),
// reduce only emit last result from the stream
reduce(() => last_result)
);
function fake_fetch(data) {
let endpoint = data.api;
if (data.concatWithPrevField) {
endpoint = endpoint.concat(`${last_result[data.prevField]}`);
endpoint = endpoint.concat(`/${data.fieldName}`);
}
return fake_API_call(endpoint).pipe(
map(data => {
last_result = data;
return data;
})
);
}
}
function fake_API_call(endpoint) {
console.log(`making API call to ${endpoint}`);
return interval(1000).pipe(
take(1),
map(data => {
switch (endpoint) {
case "/api/allGeneTables/":
return {
positions: 10,
index: 12,
name: "triosephaspate"
};
case "/api/tables/10/profiles":
return {
lipidprofile: 15,
maxvalue: 12,
name: "triosephaspate",
length: 232
};
case "/api/tables/superpositions/232/identical":
return "Hurray, I am the final result";
default:
console.error("This endpoint doesn't exist");
}
})
);
}
Upvotes: 0
Reputation: 40647
You could use the expand
operator for this type of operation. expand
recursively calls the provided function
I will give a simple example and you can do the rest:
const data = [{ id: 1 }, { id: 2 }];
const source = from(data);
source
.pipe(
//recursively call supplied function
expand((val, i) => {
console.log(`Passed value: `, val, i);
// prepare your next value here
return of(data[i+1]);
}),
//call N times (data.length)
take(data.length)
)
.subscribe(val => console.log(`RESULT: `, val));
stackblitz to play around: https://stackblitz.com/edit/typescript-kfkrga?file=index.ts&devtoolsheight=100
Upvotes: 1
Reputation: 1685
You should go with recursion in order to make this work for N number (not to hardcode things...). Easier to understand is to do this with Promises:
// Array of apis
const data: Array<{ api: string, fieldName?: string, concatWithPrevField?: boolean, prevField?: string }> = [];
// Create api call (convert it to a promise)
const createApiCall = (url) => {
return this.dhs.getGeneData(url).toPromise();
};
const overall = data.reduce((prevCall, nextAPI) => {
return prevCall.then((prevResult) => {
let url = nextAPI.api;
if (nextAPI.concatWithPrevField) {
url += `${prevResult[nextAPI.prevField]}/${nextAPI.fieldName}`
}
return createApiCall(url);
});
}, Promise.resolve());
// Subscribe to start...
overall.then(() => {
});
Ofc same can be done wih Observaables (with same approach or expand operator)
Upvotes: -1
Reputation: 14750
In general, to use results from one request to form and execute another request, you can chain them together using switchMap
:
function httpCall_1(id) { ... }
function httpCall_2(id) { ... }
function httpCall_3(id) { ... }
myFinalData$ = httpCall_1('abc').pipe(
switchMap(resp1 => httpCall_2(resp1.id)),
switchMap(resp2 => httpCall_3(resp2.id))
);
If calls further down the chain need access to prior responses, you can nest like this:
function httpCall_1(id) { ... }
function httpCall_2(id) { ... }
function httpCall_3(id1, id2) { ... }
myFinalData$ = httpCall_1('abc').pipe(
switchMap(resp1 => httpCall_2(resp1.id).pipe(
switchMap(resp2 => httpCall_3(resp1.id, resp2.id))
))
);
Upvotes: 2