What are response codes and how do they differ from each other?

👨‍💻 Frontend Developer 🟡 Often Asked 🎚️ Medium
#HTTP #API #Web

Short Answer

HTTP response codes are three-digit numbers that the server sends to the client to indicate the result of request processing:

  1. 1xx — Informational — request received, processing continues 📡
  2. 2xx — Success — request successfully processed ✅
  3. 3xx — Redirection — additional actions required 🔄
  4. 4xx — Client Error — error in client request ❌
  5. 5xx — Server Error — server failed to process request 💥
// Examples of popular codes
200 // OK — everything is fine
404 // Not Found — page not found
500 // Internal Server Error — server error
401 // Unauthorized — authorization required

Full Answer

HTTP response codes are like traffic lights in the world of web development. They tell us what’s happening with our request! 🚦

Response Code Classification

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)

1xx — Informational Codes (100-199)

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
});

2xx — Success Codes (200-299)

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
});

3xx — Redirections (300-399)

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');
  }
});

4xx — Client Errors (400-499)

“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);
  }
}

5xx — Server Errors (500-599)

“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)
      );
    }
  }
}
CodeNameMeaningWhen to use
200OKSuccessful requestGET, PUT requests
201CreatedResource createdPOST requests
204No ContentSuccess without contentDELETE requests
301Moved PermanentlyPermanent redirectURL change
304Not ModifiedNot modifiedCaching
400Bad RequestMalformed requestValidation errors
401UnauthorizedNot authorizedLogin needed
403ForbiddenAccess deniedNo permissions
404Not FoundNot foundNon-existent resource
500Internal Server ErrorServer errorServer problems

Practical Handling Examples

Universal Response Handler

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' })
});

React Hook for API

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>
  );
}

Best Practices

1. Proper Error Handling

// ✅ 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!
}

2. Using Correct Codes on Server

// 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' 
    });
  }
});

Common Mistakes

Incorrect Code Usage

// ❌ 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);
});

Ignoring Response Codes

// ❌ 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));

Simple Rules

  1. 1xx — information, continue 📡
  2. 2xx — success, everything’s great! ✅
  3. 3xx — redirection, go there 🔄
  4. 4xx — your error, fix the request ❌
  5. 5xx — our error, try later 💥
  6. Always check response codes in client 🔍
  7. Use correct codes on server 🎯
  8. Handle errors gracefully 🛡️

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 💪