Playground API
DocsStatsBlogStudio
Playground API

The free, stateful mock REST & GraphQL API sandbox where visitor mutations actually persist in an isolated session overlay without sign-up.

Public API Online (99.98%)

Endpoints

  • /api/v1/posts
  • /api/v1/users
  • /api/v1/comments
  • /api/v1/todos
  • /api/v1/graphql

Simulations

  • JWT Auth & Roles
  • Stripe Payments
  • Virtual Email & OTP
  • Latency & Chaos
  • Realtime Webhooks

Articles & Guides

Deep Dives
  • All Feature Articles→
  • React CRUD Guide
  • Why Static APIs Fail
  • Mocking Stateful Auth
  • WebSockets & SSE

Ecosystem

  • Quickstart Guide
  • TypeScript SDK
  • OpenAPI Spec
  • Postman Collection
  • GitHub Repository

© 2026 Playground API. Created by Nilesh Kumar. Open source under ISC license.

Powered by Express 5, Prisma & Neon PostgreSQLllms.txt
HomeBlogHow to Test API Error States in React Without a Real Backend
Feature Deep Dive4 min read•2026-02-24

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

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
Continue Reading
Previous Article
What If Your Mock API Actually Remembered Your POST Requests?
Next Article
How to Test Slow APIs and Network Latency in Frontend Applications

Try Playground API in Your Own App

Stateful mock REST & GraphQL API with private sandbox overlays.

Quickstart GuideOpen Interactive Studio
Table of Contents
0%
  • The Common HTTP Error Codes Every React App Must Handle
  • How to Simulate API Errors on Demand
  • Building a Robust React Error Boundary & Retry Component
  • 1. The Data Fetcher with Error Parsing (userService.js)
  • 2. The React Component with Status-Specific UI States
  • 3 Golden Rules for Frontend API Error Handling
  • Conclusion
Feature Deep Dives

In-depth technical guides exploring Playground API features — real-time subscriptions, stateful sandboxes, schema validation, latency simulation, and developer tooling.

Browse All Articles