T
TanStack10mo ago
metropolitan-bronze

What is the best Query and mutation code format ?

HI everyone ive been using tanstack query now for a little while and i love it its amazing. However i realised i have been querying data in a very poor way, doing it all in the same page with the html. I have now learned creating custom hooks with error handling. My question is what is your recommend format and how to write custom hooks your way or is there a perfect way? How can i imporve my code below ?
type Member = {
id: string;
name: string;
};

type NewMember = {
name: string;
};

const Axios: AxiosInstance = axios.create({baseURL: "https://retoolapi.dev/Zx11Hr/data"})

const get = async (): Promise<Member[]> => {
try {
const response = await Axios.get("");
const data: Member[] = response.data;
return data;
} catch (error) {
const message = (error as AxiosError<{message: string}>).response?.data?.message;
console.log("Message", message);
throw error;
}
};

export const UseGet = () => {
return useQuery<Member[], AxiosError>({
queryKey: ["members"],
queryFn: () => get(),
onSuccess: (data: Member[]) => console.log("Success", data),
onError: (error: AxiosError) => console.log("Error", error),
} as UseQueryOptions<Member[] ,AxiosError>)
};

const create = async(newMember: NewMember): Promise<Member> => {
try {
const response = await Axios.post("", newMember)
const data:Member = response.data;
return data
} catch (error) {
const message = (error as AxiosError<{message: string}>).response?.data?.message
console.log("Error", message)
throw error;
}
}

export const useCreate = () => {
return useMutation<Member, AxiosError, NewMember>({
mutationFn: (data:NewMember) => create(data),
onSuccess: (data: Member) => console.log("Success", data),
onError: (error: AxiosError) => console.log("Error", error)
})
}
type Member = {
id: string;
name: string;
};

type NewMember = {
name: string;
};

const Axios: AxiosInstance = axios.create({baseURL: "https://retoolapi.dev/Zx11Hr/data"})

const get = async (): Promise<Member[]> => {
try {
const response = await Axios.get("");
const data: Member[] = response.data;
return data;
} catch (error) {
const message = (error as AxiosError<{message: string}>).response?.data?.message;
console.log("Message", message);
throw error;
}
};

export const UseGet = () => {
return useQuery<Member[], AxiosError>({
queryKey: ["members"],
queryFn: () => get(),
onSuccess: (data: Member[]) => console.log("Success", data),
onError: (error: AxiosError) => console.log("Error", error),
} as UseQueryOptions<Member[] ,AxiosError>)
};

const create = async(newMember: NewMember): Promise<Member> => {
try {
const response = await Axios.post("", newMember)
const data:Member = response.data;
return data
} catch (error) {
const message = (error as AxiosError<{message: string}>).response?.data?.message
console.log("Error", message)
throw error;
}
}

export const useCreate = () => {
return useMutation<Member, AxiosError, NewMember>({
mutationFn: (data:NewMember) => create(data),
onSuccess: (data: Member) => console.log("Success", data),
onError: (error: AxiosError) => console.log("Error", error)
})
}
4 Replies
adverse-sapphire
adverse-sapphire10mo ago
The Query Options API
v5 brought a new, powerful API, especially if you're using React Query with TypeScript...
compatible-crimson
compatible-crimson10mo ago
I've realised this is v4 query code, but I'm gonna give you advice for v5 mostly as it's a better api you should update to. 1. You don't need the explicit return types, the inferred return types are usually more accurate 2. The query callbacks were removed in v5 of react query, it's a bit of an anti-pattern. Dominick explains more in this post https://tkdodo.eu/blog/breaking-react-querys-api-on-purpose 3. For custom errors, you can react to the error and isError properites or use the global callback we still have .https://tkdodo.eu/blog/react-query-error-handling#the-global-callbacks 3. You can use the queryOptions helper to export options to useQuery instead of custom hooks. the query options are quite a bit more portable then custom hooks (can use seprate select fucntions for each query observer for example). See https://tkdodo.eu/blog/the-query-options-api Your mutation hook is pretty good, I usually find mutations to be less portable then queries anyway.
Breaking React Query's API on purpose
Why good API design matters, even if it means breaking existing APIs in the face of resistance.
The Query Options API
v5 brought a new, powerful API, especially if you're using React Query with TypeScript...
React Query Error Handling
After covering the sunshine cases of data fetching, it's time to look at situations where things don't go as planned and "Something went wrong..."
compatible-crimson
compatible-crimson10mo ago
Cleaned up version here:
type Member = {
id: string;
name: string;
};

type NewMember = {
name: string;
};

const retoolClient = axios.create({
baseURL: 'https://retoolapi.dev/Zx11Hr/data',
});

const get = async () => {
try {
const response = await retoolClient.get('');
const data = response.data;
return data as Member[];
} catch (error) {
const message = (error as AxiosError<{ message: string }>).response?.data
?.message;
console.log('Message', message);
throw error;
}
};

export const getMembersOptions = queryOptions({
queryKey: ['members'],
queryFn: () => get(),
});

const create = async (newMember: NewMember) => {
try {
const response = await retoolClient.post('', newMember);
const data: Member = response.data;
return data;
} catch (error) {
const message = (error as AxiosError<{ message: string }>).response?.data
?.message;
console.log('Error', message);
throw error;
}
};

export const useCreate = () => {
return useMutation({
mutationFn: (data: NewMember) => create(data),
onSuccess: (data: Member) => console.log('Success', data),
onError: (error: AxiosError) => console.log('Error', error),
});
};
type Member = {
id: string;
name: string;
};

type NewMember = {
name: string;
};

const retoolClient = axios.create({
baseURL: 'https://retoolapi.dev/Zx11Hr/data',
});

const get = async () => {
try {
const response = await retoolClient.get('');
const data = response.data;
return data as Member[];
} catch (error) {
const message = (error as AxiosError<{ message: string }>).response?.data
?.message;
console.log('Message', message);
throw error;
}
};

export const getMembersOptions = queryOptions({
queryKey: ['members'],
queryFn: () => get(),
});

const create = async (newMember: NewMember) => {
try {
const response = await retoolClient.post('', newMember);
const data: Member = response.data;
return data;
} catch (error) {
const message = (error as AxiosError<{ message: string }>).response?.data
?.message;
console.log('Error', message);
throw error;
}
};

export const useCreate = () => {
return useMutation({
mutationFn: (data: NewMember) => create(data),
onSuccess: (data: Member) => console.log('Success', data),
onError: (error: AxiosError) => console.log('Error', error),
});
};
metropolitan-bronze
metropolitan-bronzeOP10mo ago
@DogPawHat Thanks so much for this, really helpful.

Did you find this page helpful?