REST APIs can return different types of errors such as validation errors, authentication errors, authorization errors, and server errors. In a TypeScript React Native application, we can create interfaces for these error responses so that errors can be handled in a structured way.
An API error occurs when the server cannot successfully process a request.
For example:
TypeScript interfaces allow us to describe the structure of an error returned by the REST API.
interface ApiError {
success: boolean;
message: string;
}
This represents a simple error response containing a success flag and an error message.
A PHP REST API may return:
{
"success": false,
"message": "Student not found"
}
The corresponding TypeScript interface is:
interface ApiError {
success: boolean;
message: string;
}
Validation errors often contain multiple field-specific messages.
{
"success": false,
"message": "Validation failed",
"errors": {
"name": [
"Name is required"
],
"email": [
"Valid email is required"
]
}
}
interface ValidationErrors {
[field: string]: string[];
}
interface ApiError {
success: boolean;
message: string;
errors?: ValidationErrors;
}
The index signature allows different form fields to contain their own error messages.
Not every API error needs to contain field validation errors.
Therefore, errors can be optional.
interface ApiError {
success: boolean;
message: string;
errors?: {
[field: string]: string[];
};
}
HTTP 400 Bad Request usually indicates that the request
contains invalid data or is not correctly formed.
{
"success": false,
"message": "Invalid request"
}
The React Native application can display the returned message to the user.
HTTP 401 Unauthorized commonly means that authentication
is missing or invalid.
{
"success": false,
"message": "Authentication required"
}
A React Native application may redirect the user to the login screen.
HTTP 403 Forbidden generally means the user is authenticated
but does not have permission to perform the requested action.
{
"success": false,
"message": "Access denied"
}
HTTP 404 Not Found can be returned when a requested
resource does not exist.
{
"success": false,
"message": "Student not found"
}
TypeScript can use the same ApiError interface.
HTTP 422 Unprocessable Content is commonly used when
the request is understood but the submitted data fails validation.
{
"success": false,
"message": "Validation failed",
"errors": {
"email": [
"Email is required"
]
}
}
HTTP 500 Internal Server Error indicates that the server
encountered an unexpected problem.
{
"success": false,
"message": "Internal server error"
}
The server should not expose sensitive database or application details to the mobile application.
Axios provides an error object when an HTTP request fails.
try {
const response = await api.get("/students");
} catch (error) {
console.log(error);
}
We can inspect the server response through Axios error information.
catch (error) {
if (axios.isAxiosError(error)) {
console.log(
error.response?.status
);
console.log(
error.response?.data
);
}
}
The response data can contain the structured API error.
We can tell TypeScript what structure we expect in the API error response.
interface ApiError {
success: boolean;
message: string;
errors?: {
[field: string]: string[];
};
}
if (axios.isAxiosError<ApiError>(error)) {
console.log(
error.response?.data.message
);
}
catch (error) {
if (axios.isAxiosError<ApiError>(error)) {
const message =
error.response?.data.message;
console.log(message);
}
}
The application can display a user-friendly message based on the response.
catch (error) {
if (axios.isAxiosError<ApiError>(error)) {
const errors =
error.response?.data.errors;
if (errors?.email) {
console.log(errors.email[0]);
}
}
}
This can be used to show validation messages near form fields.
const [errorMessage, setErrorMessage] =
useState<string>("");
The error message can be displayed in the React Native UI.
{errorMessage !== "" && (
<Text>{errorMessage}</Text>
)}
if (axios.isAxiosError<ApiError>(error)) {
const status =
error.response?.status;
if (status === 401) {
console.log("Please login");
}
if (status === 403) {
console.log("Access denied");
}
if (status === 404) {
console.log("Record not found");
}
if (status === 422) {
console.log("Validation failed");
}
}
Sometimes the server does not return an HTTP response at all. For example, the device may have no internet connection.
if (axios.isAxiosError(error)) {
if (!error.response) {
console.log(
"Network error or server unavailable"
);
}
}
Some APIs may return an application-specific error code.
interface ApiError {
success: boolean;
message: string;
code?: string;
}
Example:
{
"success": false,
"message": "Email already exists",
"code": "EMAIL_EXISTS"
}
const getApiErrorMessage = (
error: unknown
): string => {
if (axios.isAxiosError<ApiError>(error)) {
return (
error.response?.data.message ??
"Request failed"
);
}
return "Something went wrong";
};
A reusable function prevents repeated error-handling code.
try {
await api.get("/students");
} catch (error) {
const message =
getApiErrorMessage(error);
setErrorMessage(message);
}
The same function can be used by multiple screens.
A registration API may return field-specific validation errors.
interface ApiError {
success: boolean;
message: string;
errors?: {
name?: string[];
email?: string[];
mobile?: string[];
password?: string[];
};
}
React Native can display each error next to the appropriate input field.
The PHP server should return safe error messages to the mobile application.
Avoid returning:
Prefer:
{
"success": false,
"message": "Internal server error"
}
error.response.data without checking for a response.any for every error.interface ValidationErrors {
[field: string]: string[];
}
interface ApiError {
success: boolean;
message: string;
code?: string;
errors?: ValidationErrors;
}
This structure can handle simple errors, application error codes, and field-level validation errors.
import axios from "axios";
interface ApiError {
success: boolean;
message: string;
errors?: {
[field: string]: string[];
};
}
const loadStudents = async () => {
try {
const response =
await api.get("/students");
console.log(response.data);
} catch (error) {
if (axios.isAxiosError<ApiError>(error)) {
const status =
error.response?.status;
const data =
error.response?.data;
if (status === 401) {
console.log("Please login");
} else if (status === 403) {
console.log("Access denied");
} else if (status === 404) {
console.log("Students not found");
} else {
console.log(
data?.message ??
"Request failed"
);
}
} else {
console.log("Unexpected error");
}
}
};
React Native
↓
Axios Request
↓
PHP REST API
↓
Validation / Authentication
↓
HTTP Error Status
↓
JSON Error Response
↓
Axios Error
↓
TypeScript ApiError
↓
React Native Error Message
For example, if a registration request contains an invalid email,
the PHP API can return HTTP 422 with a structured validation response.
Axios receives the response, TypeScript describes its structure using
ApiError, and React Native can display the appropriate
message to the user.
{
"success": false,
"message": "Validation failed",
"errors": {
"email": [
"Valid email is required"
]
}
}
This creates a consistent and type-safe error-handling system between the PHP REST API and React Native application.
axios.isAxiosError() to identify Axios errors.Question: Which Axios property normally contains the server response when an HTTP request fails?