API requests can fail because of network problems, invalid data, authentication errors, server errors or unavailable APIs. Axios provides useful information that allows a React Native application to handle these errors properly.
A mobile application depends on network communication. The API may not always respond successfully.
React Native
↓
Axios
↓
Network/API
↓
Success or Error
↓
Handle Result
↓
UI
try {
const response =
await axios.get(API_URL);
console.log(
response.data
);
} catch (error) {
console.log(
error.message
);
}
The catch block runs when Axios encounters an error.
When the server responds with an HTTP error status, Axios usually provides the response through error.response.
catch (error) {
console.log(
error.response
);
}
catch (error) {
console.log(
error.response?.status
);
}
For example, the API may return 400, 401, 403, 404, 422 or 500.
catch (error) {
console.log(
error.response?.data
);
}
A PHP API can return a structured JSON error message that React Native can display.
Suppose the API returns:
{
"success": false,
"message": "Student not found"
}
React Native can read the message using:
const message =
error.response?.data?.message;
console.log(message);
catch (error) {
if (error.response) {
console.log(
"Server responded"
);
console.log(
error.response.status
);
}
}
This means the request reached a server and the server returned an HTTP response.
A network error can happen when the device cannot communicate with the server.
catch (error) {
if (!error.response) {
console.log(
"Network error"
);
}
}
Possible causes include no internet connection, an incorrect server URL, server downtime or connection problems.
Axios can provide information about the request when a request was made but no response was received.
catch (error) {
if (error.request &&
!error.response) {
console.log(
"No response received"
);
}
}
catch (error) {
console.log(
error.message
);
}
The message can help during development, but production applications should display a user-friendly message instead of exposing technical details.
HTTP 400 means the request is invalid or malformed.
catch (error) {
if (
error.response?.status === 400
) {
setMessage(
"Invalid request"
);
}
}
HTTP 401 indicates that authentication is required or the provided credentials/token are invalid.
if (
error.response?.status === 401
) {
setMessage(
"Please login again"
);
}
HTTP 403 means the user is authenticated or recognized but does not have permission to perform the requested operation.
if (
error.response?.status === 403
) {
setMessage(
"You do not have permission"
);
}
HTTP 404 means that the requested resource was not found.
if (
error.response?.status === 404
) {
setMessage(
"Record not found"
);
}
HTTP 422 is commonly used when the request format is understood but the submitted data fails validation.
if (
error.response?.status === 422
) {
setMessage(
error.response.data?.message ||
"Please check your input"
);
}
HTTP 500 indicates a server-side error.
if (
error.response?.status === 500
) {
setMessage(
"Server error. Please try again later."
);
}
The server should log technical details instead of sending database or PHP error details to the mobile application.
catch (error) {
const status =
error.response?.status;
if (status === 400) {
setMessage(
"Invalid request"
);
} else if (status === 401) {
setMessage(
"Please login again"
);
} else if (status === 403) {
setMessage(
"Access denied"
);
} else if (status === 404) {
setMessage(
"Data not found"
);
} else if (status === 422) {
setMessage(
"Invalid data"
);
} else if (status >= 500) {
setMessage(
"Server error"
);
} else {
setMessage(
"Something went wrong"
);
}
}
const [errorMessage, setErrorMessage] =
useState("");
try {
const response =
await axios.get(API_URL);
setData(
response.data.data
);
setErrorMessage("");
} catch (error) {
setErrorMessage(
"Unable to load data"
);
}
{errorMessage !== "" && (
<Text>
{errorMessage}
</Text>
)}
The user should receive a clear and simple message.
A retry button can be useful when a temporary network problem occurs.
async function loadStudents() {
setErrorMessage("");
try {
const response =
await axios.get(API_URL);
setStudents(
response.data.data || []
);
} catch (error) {
setErrorMessage(
"Unable to load students"
);
}
}
<Button
title="Retry"
onPress={loadStudents}
/>
try {
const response =
await axios.get(
PROFILE_URL,
{
headers: {
Authorization:
`Bearer ${token}`
}
}
);
} catch (error) {
if (
error.response?.status === 401
) {
// Clear local authentication
// and navigate to login
console.log(
"Authentication expired"
);
}
}
function getApiErrorMessage(
error
) {
if (error.response) {
return (
error.response.data?.message ||
"Server error"
);
}
if (error.request) {
return "No response from server";
}
return (
error.message ||
"Something went wrong"
);
}
This function can be reused across multiple API screens.
try {
const response =
await axios.get(API_URL);
setStudents(
response.data.data || []
);
} catch (error) {
setErrorMessage(
getApiErrorMessage(error)
);
}
async function loadStudents() {
setLoading(true);
setErrorMessage("");
try {
const response =
await axios.get(API_URL);
setStudents(
response.data.data || []
);
} catch (error) {
setErrorMessage(
getApiErrorMessage(error)
);
} finally {
setLoading(false);
}
}
The finally block makes sure the loading state is reset whether the request succeeds or fails.
async function getStudents() {
setLoading(true);
setErrorMessage("");
try {
const response =
await axios.get(
API_URL
);
setStudents(
response.data.data || []
);
} catch (error) {
if (error.response) {
const status =
error.response.status;
if (status === 401) {
setErrorMessage(
"Please login again"
);
} else if (status === 403) {
setErrorMessage(
"Access denied"
);
} else if (status === 404) {
setErrorMessage(
"Students not found"
);
} else if (status === 422) {
setErrorMessage(
"Invalid request data"
);
} else if (status >= 500) {
setErrorMessage(
"Server error"
);
} else {
setErrorMessage(
error.response.data?.message ||
"Request failed"
);
}
} else if (error.request) {
setErrorMessage(
"No response from server"
);
} else {
setErrorMessage(
"Unable to complete request"
);
}
} finally {
setLoading(false);
}
}
<?php
header(
"Content-Type: application/json"
);
$id = filter_input(
INPUT_GET,
"id",
FILTER_VALIDATE_INT
);
if (!$id) {
http_response_code(400);
echo json_encode([
"success" => false,
"message" =>
"Valid student ID is required"
]);
exit;
}
try {
// Database operation
} catch (PDOException $e) {
error_log(
$e->getMessage()
);
http_response_code(500);
echo json_encode([
"success" => false,
"message" =>
"Internal server error"
]);
exit;
}
Technical database errors should be logged on the server and not exposed to the mobile application.
import React, {
useEffect,
useState
} from "react";
import {
View,
Text,
Button,
ActivityIndicator
} from "react-native";
import axios from "axios";
const API_URL =
"https://example.com/api/students.php";
export default function StudentList() {
const [students, setStudents] =
useState([]);
const [loading, setLoading] =
useState(false);
const [errorMessage, setErrorMessage] =
useState("");
async function loadStudents() {
setLoading(true);
setErrorMessage("");
try {
const response =
await axios.get(API_URL);
setStudents(
response.data.data || []
);
} catch (error) {
if (error.response) {
const status =
error.response.status;
if (status === 401) {
setErrorMessage(
"Please login again."
);
} else if (status === 403) {
setErrorMessage(
"Access denied."
);
} else if (status === 404) {
setErrorMessage(
"No student data found."
);
} else if (status >= 500) {
setErrorMessage(
"Server error. Try again later."
);
} else {
setErrorMessage(
error.response.data?.message ||
"Request failed."
);
}
} else if (error.request) {
setErrorMessage(
"Please check your internet connection."
);
} else {
setErrorMessage(
"Something went wrong."
);
}
} finally {
setLoading(false);
}
}
useEffect(() => {
loadStudents();
}, []);
return (
<View style={{
flex: 1,
padding: 20
}}>
{loading && (
<ActivityIndicator
size="large"
/>
)}
{errorMessage !== "" && (
<View>
<Text>
{errorMessage}
</Text>
<Button
title="Retry"
onPress={loadStudents}
/>
</View>
)}
{!loading &&
errorMessage === "" && (
<Text>
Students loaded:
{students.length}
</Text>
)}
</View>
);
}
| Technical Situation | User Message |
|---|---|
| Network unavailable | Please check your internet connection. |
| 401 | Please login again. |
| 403 | You do not have permission. |
| 404 | Requested data was not found. |
| 422 | Please check your entered information. |
| 500 | Server error. Please try again later. |
React Native
↓
Axios Request
↓
API Server
↓
┌───────────────┐
│ Success? │
└───────────────┘
↓
Yes / No
↓
Success → Display Data
Error
↓
Check response
↓
Check status
↓
401 / 403 / 404 / 422 / 500
↓
Show User-Friendly Message
No Response
↓
Network Error
↓
Retry
Proper Axios error handling makes React Native applications more reliable, easier to debug and more user-friendly.
Question: Which Axios property commonly contains the server response when an HTTP error occurs?