Filed under · Frontend · 2026-08-18 · 7 min read
How I handle API errors in the frontend
A message like "Something went wrong" is easy to implement but not always useful. Different failures should lead to different user actions.
Network error
A network error means the browser may not have received a response at all.
- The internet connection is unavailable.
- DNS lookup failed.
- The server is unavailable.
- CORS configuration blocked the request.
- The request timed out.
A useful message could be: "Couldn't reach the server. Check your connection and try again."
401 Unauthorized
This usually means authentication is missing or no longer valid. Refresh the session if the application supports it; otherwise, ask the user to sign in again.
403 Forbidden
The server knows who the user is, but they do not have permission to perform that action. Do not treat this exactly like 401. A useful message is: "You don't have permission to perform this action."
404 Not Found
The requested resource does not exist. For a project page, this might mean showing a proper Not Found state instead of a generic application crash.
Validation errors
A 400 or 422 response may contain field-specific problems.
json{
"errors": {
"email": "Enter a valid email address",
"password": "Password must be at least 8 characters"
}
}Instead of showing one toast saying "Request failed", display the errors beside the fields the user can fix.
500 Server Error
The frontend usually cannot fix this. Show a safe message to the user and log enough diagnostic information on the server. Do not expose stack traces or internal implementation details in the UI.
A simple request pattern
typescriptasync function loadProfile() {
const response = await fetch("/api/profile");
if (response.status === 401) {
throw new Error("AUTH_REQUIRED");
}
if (response.status === 403) {
throw new Error("FORBIDDEN");
}
if (response.status === 404) {
throw new Error("NOT_FOUND");
}
if (!response.ok) {
throw new Error("SERVER_ERROR");
}
return response.json();
}The exact implementation can be centralized in an API client, but the important idea is that the application understands the difference between failures. Network failures can be handled by the caller's catch block because fetch rejects before a response exists.
An error message should tell the user what happened and, when possible, what they can do next.