T
TanStack•3y ago
flat-fuchsia

call 2 independent endpoints in one useMutation or should I use 2? What's the best practice?

I have one form and one save button, but the content of the form goes to 2 endpoints. They are somehow independent, which means they don't need result to each other to be operated successfully. Is it best that I have :
useQuery for endpoint 1
userQuery for endpoint 2
useMutation for endpoint 1
useMutation for endpoint 2
useQuery for endpoint 1
userQuery for endpoint 2
useMutation for endpoint 1
useMutation for endpoint 2
or
useQuery -> fetch endpoint 1, and fetch endpoint 2 both?
useMutation -> do change to endpoint 1 and endpoint 2 both?
useQuery -> fetch endpoint 1, and fetch endpoint 2 both?
useMutation -> do change to endpoint 1 and endpoint 2 both?
Thanks!
8 Replies
flat-fuchsia
flat-fuchsiaOP•3y ago
I kinda think one endpoint per useQuery/useMutation is better cos we can have different error message if we choose to. But I'd like to hear different opinion.
modern-teal
modern-teal•3y ago
Might be a personal preference but I would lean towards the second option. That makes the code simpler: one isLoading state, one data etc... You can still have different error messages: : inside the query function, if the first query fails then throw error 1, if the second query fails then throw error2 etc...
flat-fuchsia
flat-fuchsiaOP•3y ago
oh, in that case should we write it like
const func = () => {
const res1 = await 1stEndPointCall();
const res2 = await 2ndEndPointCall();
return {res1, res2};
}
const func = () => {
const res1 = await 1stEndPointCall();
const res2 = await 2ndEndPointCall();
return {res1, res2};
}
Then pass this func into the useQuery? such as async()=> {return func()}? But do I get (res1, res2) in the onSuccess, and could set up the useState changes after I get it? how to throw different errors in those case in the onError? ok I actually found an example:
const [postsQuery, usersQuery] = useQueries({
queries: [
{
queryKey: ['posts'],
queryFn: () =>
axios
.get('https://jsonplaceholder.typicode.com/posts')
.then((res) => res.data),
},

{
queryKey: ['users'],
queryFn: () =>
axios
.get('https://jsonplaceholder.typicode.com/users')
.then((res) => res.data),
},
],
});

if (postsQuery.isLoading) return 'Loading Posts...';
if (usersQuery.isLoading) return 'Loading Users...';

if (postsQuery.error)
return 'An error has occurred: ' + postsQuery.error.message;

if (usersQuery.error)
return 'An error has occurred: ' + usersQuery.error.message;
const [postsQuery, usersQuery] = useQueries({
queries: [
{
queryKey: ['posts'],
queryFn: () =>
axios
.get('https://jsonplaceholder.typicode.com/posts')
.then((res) => res.data),
},

{
queryKey: ['users'],
queryFn: () =>
axios
.get('https://jsonplaceholder.typicode.com/users')
.then((res) => res.data),
},
],
});

if (postsQuery.isLoading) return 'Loading Posts...';
if (usersQuery.isLoading) return 'Loading Users...';

if (postsQuery.error)
return 'An error has occurred: ' + postsQuery.error.message;

if (usersQuery.error)
return 'An error has occurred: ' + usersQuery.error.message;
is this the good way to do it in this case? 👆
modern-teal
modern-teal•3y ago
That is pretty much first option you were describing (one query per call). As you mentioned, your query function can be like this:
() => {
const res1 = await 1stEndPointCall();
const res2 = await 2ndEndPointCall();
return {res1, res2};
}
() => {
const res1 = await 1stEndPointCall();
const res2 = await 2ndEndPointCall();
return {res1, res2};
}
But you probably want to call the apis in parallel, so you can use Promise.all():
() => {
const [res1, res2] = await Promise.all([1stEndPointCall(), 2ndEndPointCall()]);
}
() => {
const [res1, res2] = await Promise.all([1stEndPointCall(), 2ndEndPointCall()]);
}
If any of the apis fails, this will throw an exception and the error will show up in onError. If both succeed, you'll get both res1 and res2 in onSuccess. This makes the code a bit simpler imo:
const query = useQuery({
queryKey: ['posts_users'],
queryFn: () => {
const [res1, res2] = await Promise.all([1stEndPointCall(), 2ndEndPointCall()]);
return {res1, res2};
});

if (query.isLoading) return 'Loading Data...';

if (query.error)
return 'An error has occurred: ' + query.error.message;
const query = useQuery({
queryKey: ['posts_users'],
queryFn: () => {
const [res1, res2] = await Promise.all([1stEndPointCall(), 2ndEndPointCall()]);
return {res1, res2};
});

if (query.isLoading) return 'Loading Data...';

if (query.error)
return 'An error has occurred: ' + query.error.message;
flat-fuchsia
flat-fuchsiaOP•3y ago
Jaha... is this the same for useMutation in this case?
modern-teal
modern-teal•3y ago
I would do the same, yeah
flat-fuchsia
flat-fuchsiaOP•3y ago
@julien Sorry to bother you, when I put onSuccess: ({res1, res2}) then I am getting both as undefined. Why is that? For some reason I don't seem to get all the result, if I follow the query.data instead of using onSuccess, I would only get res1, but not res2.... Makes me wonder whether we can't use promise.all in useQuery? even strange thing, sometimes if I try to console.log, I'd actually see both of them... but if I refresh the page, I would get either res1, or just undefined.
modern-teal
modern-teal•3y ago
can you show the code? (the queryFn as well)

Did you find this page helpful?