How to Test API Error States in React Without a Real Backend

Learn how to test 400, 401, 403, 404, and 500 API error states, React error boundaries, and retry logic without intentionally crashing a backend server.

NK
Nilesh Kumar
Creator of Playground API
How to Test API Error States in React Without a Real Backend

How to Test API Error States in React Without a Real Backend

Suggested URL Slug: test-api-error-states-in-react

Primary Keyword: test API errors in React

Secondary Keywords: React error handling, mock API errors, HTTP status codes testing, React error boundary API

Meta Description: Learn how to test 400, 401, 403, 404, and 500 API error states, React error boundaries, and retry logic without intentionally crashing a backend server.

Suggested Dev.to Tags: #react, #webdev, #javascript, #testing


Almost every frontend developer writes code assuming the "Happy Path":

  1. The API is online.
  2. The network connection is stable.
  3. The server responds with 200 OK in 50 milliseconds.
  4. The JSON response matches the expected TypeScript type definition perfectly.

Then your application goes to production, and real life happens:

  • A database query times out, returning a 500 Internal Server Error.
  • A session expires, returning a 401 Unauthorized.
  • An invalid query parameter triggers a 400 Bad Request.
  • A user visits an expired URL, receiving a 404 Not Found.

If your React application hasn't been tested against these error states, the user is often greeted with a blank white screen, an unhandled Promise rejection, or a broken spinner that spins forever.

How can you test how your UI handles errors without manually modifying backend code to throw exceptions or turning off your Wi-Fi?


The Common HTTP Error Codes Every React App Must Handle

Before diving into code, let's categorize the common HTTP status codes frontend applications encounter:

Status CodeMeaningExpected Frontend Behavior
400 Bad RequestMalformed input / validation failureDisplay field-level inline error messages.
401 UnauthorizedMissing or invalid auth tokenRedirect to Login modal or trigger silent token refresh.
403 ForbiddenInsufficient permissions / RBACShow "Access Denied / Upgrade Plan" screen.
404 Not FoundResource does not existRender a friendly "Item Not Found" card with a back button.
429 Too Many RequestsRate limit exceededDisplay countdown timer based on Retry-After header.
500 Internal Server ErrorUnhandled server crashRender an Error Boundary fallback with a "Retry" button.

How to Simulate API Errors on Demand

Instead of editing server routes to throw fake errors or hardcoding if (debug) throw new Error() inside React components, you can use on-demand error simulation headers and query parameters.

Playground API by Niles Labs supports built-in error simulation using either query parameters or HTTP headers on any endpoint:

javascript
1
// Via Query Parameter:
2
GET https://playground.nileslabs.com/api/v1/posts?_status=500
3
GET https://playground.nileslabs.com/api/v1/users/1?_status=404
4
5
// Via HTTP Header:
6
X-Simulate-Status: 403
7
X-Simulate-Status: 429

When this parameter or header is sent, the server immediately halts standard execution and responds with the requested HTTP status code and a structured RFC 7807 error payload.


Building a Robust React Error Boundary & Retry Component

Let's build an interactive user profile card in React that gracefully handles 404 Not Found, 500 Server Error, and network failures with automated retry logic.

1. The Data Fetcher with Error Parsing (userService.js)

javascript
1
// src/services/userService.js
2
const BASE_URL = 'https://playground.nileslabs.com/api/v1';
3
4
export async function fetchUserProfile(userId, forcedStatus = null) {
5
// If forcedStatus is provided, append ?_status=XXX for testing
6
const url = forcedStatus
7
? ${BASE_URL}/users/${userId}?_status=${forcedStatus}
8
: ${BASE_URL}/users/${userId};
9
10
const response = await fetch(url);
11
12
if (!response.ok) {
13
let errorDetails = 'Unknown error occurred.';
14
try {
15
const errorJson = await response.json();
16
errorDetails = errorJson.message || errorJson.error || response.statusText;
17
} catch {
18
errorDetails = response.statusText;
19
}
20
21
const error = new Error(Request failed with status ${response.status});
22
error.status = response.status;
23
error.details = errorDetails;
24
throw error;
25
}
26
27
return response.json();
28
}

2. The React Component with Status-Specific UI States

jsx
1
// src/components/UserProfileCard.jsx
2
import React, { useState, useEffect } from 'react';
3
import { fetchUserProfile } from '../services/userService';
4
5
export default function UserProfileCard({ userId }) {
6
const [user, setUser] = useState(null);
7
const [loading, setLoading] = useState(true);
8
const [error, setError] = useState(null);
9
const [simulatedStatus, setSimulatedStatus] = useState('');
10
11
const loadUser = async () => {
12
try {
13
setLoading(true);
14
setError(null);
15
const data = await fetchUserProfile(userId, simulatedStatus || null);
16
setUser(data);
17
} catch (err) {
18
setError({
19
status: err.status,
20
message: err.details || err.message,
21
});
22
setUser(null);
23
} finally {
24
setLoading(false);
25
}
26
};
27
28
useEffect(() => {
29
loadUser();
30
}, [userId, simulatedStatus]);
31
32
return (
33
<div style={{ maxWidth: '480px', margin: '2rem auto', border: '1px solid #cbd5e1', borderRadius: '8px', padding: '1.5rem', fontFamily: 'sans-serif' }}>
34
<h3>👤 User Profile Inspector</h3>
35
36
{/* Simulator Toolbar for QA / Testing */}
37
<div style={{ background: '#f8fafc', padding: '10px', borderRadius: '6px', marginBottom: '1rem' }}>
38
<label style={{ fontSize: '13px', fontWeight: 'bold' }}>Simulate API Status: </label>
39
<select
40
value={simulatedStatus}
41
onChange={(e) => setSimulatedStatus(e.target.value)}
42
style={{ marginLeft: '8px', padding: '4px 8px' }}
43
>
44
<option value="">Normal (200 OK)</option>
45
<option value="400">400 Bad Request</option>
46
<option value="401">401 Unauthorized</option>
47
<option value="403">403 Forbidden</option>
48
<option value="404">404 Not Found</option>
49
<option value="500">500 Internal Server Error</option>
50
</select>
51
</div>
52
53
{/* Loading State */}
54
{loading && <div style={{ color: '#64748b' }}>⏳ Fetching user profile...</div>}
55
56
{/* Error State Handler */}
57
{!loading && error && (
58
<div style={{
59
backgroundColor: error.status === 404 ? '#fffbeb' : '#fef2f2',
60
border: 1px solid ${error.status === 404 ? '#fde68a' : '#fecaca'},
61
borderRadius: '6px',
62
padding: '1rem',
63
color: error.status === 404 ? '#92400e' : '#991b1b'
64
}}>
65
<h4>
66
{error.status === 404 ? '🔍 User Not Found (404)' :
67
error.status === 401 ? '🔒 Session Expired (401)' :
68
error.status === 403 ? '🚫 Access Denied (403)' :
69
⚠️ Server Error (${error.status})}
70
</h4>
71
<p style={{ margin: '8px 0', fontSize: '14px' }}>{error.message}</p>
72
73
<button
74
onClick={() => { setSimulatedStatus(''); loadUser(); }}
75
style={{ padding: '6px 12px', background: '#0f172a', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer' }}
76
>
77
🔄 Reset & Retry
78
</button>
79
</div>
80
)}
81
82
{/* Success State */}
83
{!loading && !error && user && (
84
<div>
85
<h4>{user.name} (@{user.username})</h4>
86
<p>📧 {user.email}</p>
87
<p>🏢 {user.company?.name}</p>
88
</div>
89
)}
90
</div>
91
);
92
}

3 Golden Rules for Frontend API Error Handling

  1. Never Show Raw JSON Exceptions to End Users:

Always parse backend error payloads into human-readable action steps (e.g. "We couldn't find that article. Check the link or return home.").

  1. Always Provide a Recovery Action:

Every error state should have a "Retry", "Refresh", or "Back to Safety" CTA button. Never leave a user stuck on a dead-end screen.

  1. Log Unhandled Errors to Monitoring (Sentry / LogRocket):

If an error is unexpected (such as a 500 error), catch it in a top-level React <ErrorBoundary> component and dispatch the telemetry before rendering a fallback card.


Conclusion

Testing error states is just as important as testing happy paths. By leveraging simulated HTTP error statuses in your sandbox API, you can stress-test edge cases, error boundaries, and user feedback mechanisms before your code ever touches production.

To test error states and simulate HTTP failures in your application, start with Playground API by Niles Labs.

Tags:#react#webdev#javascript#testing

Try Playground API in Your Own App

Stateful mock REST & GraphQL API with private sandbox overlays.