HTTP response codes are three-digit numbers that the server sends to the client to indicate the result of request processing:
// Examples of popular codes
200 // OK — everything is fine
404 // Not Found — page not found
500 // Internal Server Error — server error
401 // Unauthorized — authorization requiredHTTP response codes are like traffic lights in the world of web development. They tell us what’s happening with our request! 🚦
HTTP codes consist of three digits, where the first digit determines the response class:
// HTTP code structure
[Class][Subclass][Specific code]
1-5 0-9 0-9
// Examples
200 // Class 2 (success), code 00 (OK)
404 // Class 4 (client error), code 04 (Not Found)These codes say: “I received your request, working on it!” 🔄
// Main informational codes
100 // Continue — continue sending
101 // Switching Protocols — protocol switching
102 // Processing — request is being processed
103 // Early Hints — early hints
// Usage example
fetch('/api/upload', {
method: 'POST',
body: largeFile
}).then(response => {
// May receive 100 Continue before main response
console.log(response.status); // 200 after completion
});The most pleasant codes — everything went perfectly! 🎉
// Popular success codes
200 // OK — standard success
201 // Created — resource created
202 // Accepted — request accepted for processing
204 // No Content — success but no content
206 // Partial Content — partial content
// Usage examples
// GET request
fetch('/api/users')
.then(response => {
console.log(response.status); // 200 OK
return response.json();
});
// POST request (creation)
fetch('/api/users', {
method: 'POST',
body: JSON.stringify(userData)
}).then(response => {
console.log(response.status); // 201 Created
});
// DELETE request
fetch('/api/users/123', {
method: 'DELETE'
}).then(response => {
console.log(response.status); // 204 No Content
});Codes say: “What you’re looking for is in another place!” 🗺️
// Redirection codes
300 // Multiple Choices — multiple options
301 // Moved Permanently — permanent redirect
302 // Found — temporary redirect
304 // Not Modified — not modified
307 // Temporary Redirect — temporary redirect
308 // Permanent Redirect — permanent redirect
// Handling redirects
fetch('/old-page', {
redirect: 'follow' // automatically follow redirects
}).then(response => {
if (response.redirected) {
console.log('Redirected to:', response.url);
}
});
// Caching with 304
fetch('/api/data', {
headers: {
'If-None-Match': etag // cache check
}
}).then(response => {
if (response.status === 304) {
console.log('Data unchanged, using cache');
}
});“You did something wrong!” — these codes say 🤦♂️
// Popular client errors
400 // Bad Request — malformed request
401 // Unauthorized — authorization required
403 // Forbidden — access denied
404 // Not Found — not found
405 // Method Not Allowed — method not allowed
409 // Conflict — data conflict
422 // Unprocessable Entity — invalid data
429 // Too Many Requests — too many requests
// Handling client errors
async function handleRequest() {
try {
const response = await fetch('/api/protected', {
headers: {
'Authorization': `Bearer ${token}`
}
});
switch (response.status) {
case 400:
throw new Error('Bad request');
case 401:
// Redirect to login page
window.location.href = '/login';
break;
case 403:
throw new Error('Access denied');
case 404:
throw new Error('Resource not found');
case 422:
const errors = await response.json();
console.log('Validation errors:', errors);
break;
case 429:
throw new Error('Too many requests, try again later');
}
} catch (error) {
console.error('Error:', error.message);
}
}“It’s not your fault, we have problems!” 🔥
// Server errors
500 // Internal Server Error — internal error
501 // Not Implemented — not implemented
502 // Bad Gateway — bad gateway
503 // Service Unavailable — service unavailable
504 // Gateway Timeout — gateway timeout
505 // HTTP Version Not Supported — HTTP version not supported
// Handling server errors
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (response.status >= 500) {
throw new Error(`Server error: ${response.status}`);
}
return response;
} catch (error) {
if (i === maxRetries - 1) throw error;
// Exponential backoff
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, i) * 1000)
);
}
}
}| Code | Name | Meaning | When to use |
|---|---|---|---|
| 200 | OK | Successful request | GET, PUT requests |
| 201 | Created | Resource created | POST requests |
| 204 | No Content | Success without content | DELETE requests |
| 301 | Moved Permanently | Permanent redirect | URL change |
| 304 | Not Modified | Not modified | Caching |
| 400 | Bad Request | Malformed request | Validation errors |
| 401 | Unauthorized | Not authorized | Login needed |
| 403 | Forbidden | Access denied | No permissions |
| 404 | Not Found | Not found | Non-existent resource |
| 500 | Internal Server Error | Server error | Server problems |
class ApiClient {
async request(url, options = {}) {
try {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
...options.headers
},
...options
});
// Handle by code classes
if (response.status >= 200 && response.status < 300) {
return this.handleSuccess(response);
} else if (response.status >= 300 && response.status < 400) {
return this.handleRedirect(response);
} else if (response.status >= 400 && response.status < 500) {
throw await this.handleClientError(response);
} else if (response.status >= 500) {
throw await this.handleServerError(response);
}
} catch (error) {
console.error('Request error:', error);
throw error;
}
}
async handleSuccess(response) {
if (response.status === 204) {
return null; // No Content
}
return response.json();
}
handleRedirect(response) {
// Browser automatically handles redirects
return response;
}
async handleClientError(response) {
const error = await response.json();
switch (response.status) {
case 400:
return new Error(`Bad request: ${error.message}`);
case 401:
this.redirectToLogin();
return new Error('Authorization required');
case 403:
return new Error('Access denied');
case 404:
return new Error('Resource not found');
case 422:
return new Error(`Validation errors: ${JSON.stringify(error.errors)}`);
default:
return new Error(`Client error: ${response.status}`);
}
}
async handleServerError(response) {
return new Error(`Server error: ${response.status}`);
}
redirectToLogin() {
window.location.href = '/login';
}
}
// Usage
const api = new ApiClient();
// GET request
const users = await api.request('/api/users');
// POST request
const newUser = await api.request('/api/users', {
method: 'POST',
body: JSON.stringify({ name: 'John', email: 'john@example.com' })
});import { useState, useCallback } from 'react';
function useApi() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const request = useCallback(async (url, options = {}) => {
setLoading(true);
setError(null);
try {
const response = await fetch(url, options);
if (!response.ok) {
const errorMessage = await getErrorMessage(response);
throw new Error(errorMessage);
}
const data = response.status === 204 ? null : await response.json();
return data;
} catch (err) {
setError(err.message);
throw err;
} finally {
setLoading(false);
}
}, []);
return { request, loading, error };
}
async function getErrorMessage(response) {
const statusMessages = {
400: 'Bad request',
401: 'Authorization required',
403: 'Access denied',
404: 'Resource not found',
422: 'Data validation error',
429: 'Too many requests',
500: 'Internal server error',
502: 'Bad gateway',
503: 'Service unavailable',
504: 'Gateway timeout'
};
try {
const errorData = await response.json();
return errorData.message || statusMessages[response.status] || `Error ${response.status}`;
} catch {
return statusMessages[response.status] || `Error ${response.status}`;
}
}
// Usage in component
function UserList() {
const { request, loading, error } = useApi();
const [users, setUsers] = useState([]);
useEffect(() => {
const fetchUsers = async () => {
try {
const data = await request('/api/users');
setUsers(data);
} catch (err) {
console.error('Failed to load users:', err.message);
}
};
fetchUsers();
}, [request]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}// ✅ Good: detailed handling
async function createUser(userData) {
try {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
if (response.status === 201) {
return await response.json();
} else if (response.status === 400) {
const errors = await response.json();
throw new ValidationError(errors);
} else if (response.status === 409) {
throw new ConflictError('User already exists');
} else {
throw new Error(`Unexpected error: ${response.status}`);
}
} catch (error) {
console.error('User creation error:', error);
throw error;
}
}
// ❌ Bad: ignoring codes
async function createUserBad(userData) {
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(userData)
});
return response.json(); // Could be an error!
}// Express.js examples
app.post('/api/users', async (req, res) => {
try {
// Validation
if (!req.body.email) {
return res.status(400).json({
error: 'Email is required'
});
}
// Check existence
const existingUser = await User.findByEmail(req.body.email);
if (existingUser) {
return res.status(409).json({
error: 'User already exists'
});
}
// Creation
const user = await User.create(req.body);
res.status(201).json(user); // Created
} catch (error) {
console.error(error);
res.status(500).json({
error: 'Internal server error'
});
}
});
app.delete('/api/users/:id', async (req, res) => {
try {
const user = await User.findById(req.params.id);
if (!user) {
return res.status(404).json({
error: 'User not found'
});
}
await user.delete();
res.status(204).send(); // No Content
} catch (error) {
res.status(500).json({
error: 'Deletion error'
});
}
});// ❌ Bad: wrong codes
app.post('/api/users', (req, res) => {
if (!req.body.name) {
return res.status(500).json({ error: 'Name required' }); // Should be 400!
}
res.status(200).json({ message: 'Created' }); // Should be 201!
});
// ✅ Good: correct codes
app.post('/api/users', (req, res) => {
if (!req.body.name) {
return res.status(400).json({ error: 'Name required' });
}
const user = createUser(req.body);
res.status(201).json(user);
});// ❌ Bad: not checking status
fetch('/api/data')
.then(response => response.json()) // Could be an error!
.then(data => console.log(data));
// ✅ Good: checking status
fetch('/api/data')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));Understanding HTTP codes will help you create reliable and predictable APIs! 🚀
Want more articles for interview preparation? Subscribe to EasyAdvice, bookmark the site and improve every day 💪