I have a query that retrieves data via an SQL statement. Within that query, I have defined a success handler script that loops through the results and executes an API-request (queryExpenseById):
await queryExpenseById.trigger({
additionalScope : {
expense_id: entries[i].harvest_id
},
onSuccess: (data) => {
console.log(data);
},
onFailure: (data) => {
console.log(data.error.message);
}
});
In some cases, there are no records found and the API-request yields a HTTP 404 error with a message.
The issue I am facing is that the error is not caught in the onFailure section, but instead an error is thrown by queryExpenseId itself. My console.log() statements do not seem to be executed at all.
What am I missing here? I would like to react to the error and continue the loop through the table.
1 Like
Hi there @mprobst,
I think here’s what’s going wrong:
- You’re mixing patterns.
When you do await queryExpenseById.trigger(...), a 404 rejects the promise immediately. That happens before your onFailure callback, so your console.log in onFailure never runs.
- So you want to iterate through each response from the SQL query data, right?
You refer to entries[i], but in a Retool query Success handler the rows can't be referred to as [i]. You need to declare that data as a constable.
Here's the approach I would take:
- Make your SQL results an array
In the SQL query’s Transformer, return:
return formatDataAsArray(data);
Now self.data is guaranteed to be [{...}, {...}, ...].
- Update the Success handler: pick one clean pattern.
Sequential (simple & predictable):
for (const row of self.data) {
try {
const res = await queryExpenseById.trigger({
additionalScope: { expense_id: row.harvest_id },
});
console.log('OK:', row.harvest_id, res);
} catch (err) {
// 404 (and other HTTP errors) land here
console.log('Error for', row.harvest_id, err?.message ?? err);
// loop continues automatically
}
}
Parallel (faster, handles all results at the end):
const tasks = self.data.map((row) =>
queryExpenseById.trigger({
additionalScope: { expense_id: row.harvest_id },
})
);
const results = await Promise.allSettled(tasks);
results.forEach((r, i) => {
const id = self.data[i].harvest_id;
if (r.status === 'fulfilled') {
console.log('OK:', id, r.value);
} else {
console.log('Error for', id, r.reason?.message ?? r.reason);
}
});
I hope the above helps, and sorry if I'm completely missing the point
Hi @MiguelOrtiz ,
thanks a lot for your explanation. You hit the origin of my problem and the sequential code works like a charm.
I am still struggling with the parallel version since the map function does not seem to work with the given datasets, but during the process I am about to reconsider the whole mechanism which most likely makes this not necessary.
Regards
1 Like