Beautiful Error Handling with Tanstack Query and Axios (The best combo??)
Source: Dev.to
The Backend Services
We have a very simple architecture for this project.
- Supabase as BaaS handles authentication, session management, etc.
- Express handles authorization, database querying, and other backend services.
For Express we employ a simplified MVC architecture:
- Models – handle all database operations with Supabase.
- Views – service classes in the front‑end.
- Controllers – contain business logic.
We just use these names to ensure structure and separation of concerns.
Example Controller
export const completeProfile = async (req, res) => {
try {
const { role, userData } = req.body;
const existingProfile = await UserProfileModel.getCustomerProfile(req.user.id);
if (existingProfile) {
return res.status(400).json({
error: "ProfileError",
message: "Profile already exists",
});
}
const result = await UserProfileModel.createProfileWithEntity(
req.user.id,
role,
userData
);
res.json({
message: "Profile completed successfully",
role,
userId: req.user.id,
});
} catch (error) {
res.status(500).json({
error: "ProfileError",
message: error.message,
});
}
};
When a user tries to create a profile that already exists, the controller returns a 400 response:
{
"error": "ProfileError",
"message": "Profile already exists"
}
The Good Part: Axios
Axios is a promise‑based HTTP client that simplifies API requests compared to the native fetch API.
Using fetch
const completeProfile = async (payload) => {
const res = await fetch('/profile/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!res.ok) {
const errorData = await res.json().catch(() => ({}));
throw new Error(errorData.message || 'Failed to complete profile');
}
return res.json();
};
Using Axios
import axios from 'axios';
const completeProfile = async (payload) => {
const { data } = await axios.post('/profile/complete', payload);
return data;
};
Axios Error Object
When the response status is outside the 2xx range, Axios throws an error object like:
{
"response": {
"status": 400,
"data": {
"error": "ProfileError",
"message": "Profile already exists"
}
}
}
This eliminates the need for manual if (!res.ok) checks.
TanStack Query (formerly React Query)
Instead of handling errors with try / catch in every component, TanStack Query centralizes data fetching, caching, loading states, and error handling.
What Is TanStack Query?
TanStack Query is a powerful data‑fetching and state‑management library for React. It automatically manages:
- Loading states
- Error handling
- Retries and caching
- Background refetching
This lets you focus on building features rather than wiring boilerplate.
Using TanStack Query with Axios
import { useMutation } from '@tanstack/react-query';
const useCompleteProfile = () => {
return useMutation({
mutationFn: (payload) => completeProfile(payload),
});
};
Handling Errors in a Component
import { View, Text } from 'react-native';
import { useCompleteProfile } from './hooks';
const CompleteProfileComponent = ({ payload }) => {
const mutation = useCompleteProfile();
const handleSubmit = () => {
mutation.mutate(payload);
};
return (
<View>
{/* ...your form UI... */}
{mutation.isError && (
<Text>
{mutation.error?.response?.data?.message || 'An error occurred'}
</Text>
)}
</View>
);
};
The mutation.isError flag becomes true automatically when Axios throws an error, and the error details are accessible via mutation.error.
Images


