Web Architecture Fundamentals

Part 2: Why HTTP Status Codes Matter More Than Most Developers Think
Imagine clicking the Login button on an application.
You enter your email.
You enter your password.
You click Submit.
What happens next?
Most developers immediately think about authentication logic, database queries, and JWT tokens.
However, there is another critical piece of communication happening between the frontend and backend:
HTTP Status Codes.
Without them, the frontend would have no reliable way to understand whether a request succeeded, failed, or requires further action.
In modern applications, HTTP status codes are the language through which systems communicate.
Understanding them is essential for building reliable APIs and user-friendly applications.
Every Request Needs a Meaningful Response
Consider a login request.
POST /api/auth/login
The backend processes the request and sends a response.
But how does the frontend know what happened?
Did authentication succeed?
Was the password wrong?
Does the user exist?
Did the server crash?
This is where status codes become important.
Common Status Codes Every Developer Should Know
200 OK
The request was successful.
HTTP/1.1 200 OK
Example:
{
"token": "jwt-token",
"username": "john"
}
Frontend action:
navigate("/dashboard");
201 Created
Used when creating new resources.
Example:
POST /api/users/register
Response:
HTTP/1.1 201 Created
Meaning:
User account successfully created.
400 Bad Request
The client sent invalid data.
Example:
{
"email": "",
"password": ""
}
Response:
HTTP/1.1 400 Bad Request
Meaning:
Your request is invalid.
401 Unauthorized
Authentication failed.
Example:
HTTP/1.1 401 Unauthorized
Meaning:
Invalid username or password.
403 Forbidden
User is authenticated but lacks permission.
Example:
Student trying to access Admin Dashboard
Response:
HTTP/1.1 403 Forbidden
Meaning:
You are not allowed to access this resource.
404 Not Found
Resource doesn't exist.
GET /api/courses/999
Response:
HTTP/1.1 404 Not Found
Meaning:
Requested course not found.
500 Internal Server Error
Something went wrong on the server.
Response:
HTTP/1.1 500 Internal Server Error
Meaning:
Backend encountered an unexpected problem.
A Common Mistake
Many beginner APIs return:
{
"success": true
}
for every situation.
Even failures.
Example:
HTTP/1.1 200 OK
{
"message":"Invalid Password"
}
Technically this works.
Architecturally it is wrong.
The frontend now needs to inspect message strings to determine success or failure.
This becomes difficult to maintain.
Instead:
HTTP/1.1 401 Unauthorized
{
"message":"Invalid Password"
}
Now the response itself communicates the problem
Spring Boot Example
Let's build a proper login endpoint.
Login Controller
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@PostMapping("/login")
public ResponseEntity<?> login(
@RequestBody LoginRequest request) {
if(request.getEmail() == null ||
request.getPassword() == null) {
return ResponseEntity
.badRequest()
.body("Email and Password Required");
}
boolean authenticated =
authService.authenticate(request);
if(!authenticated) {
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.body("Invalid Credentials");
}
return ResponseEntity.ok(
"Login Successful");
}
}
Notice how each scenario returns a meaningful status code.
React Vite Example
The frontend can react differently based on status codes.
import axios from "axios";
const login = async () => {
try {
const response = await axios.post(
"/api/auth/login",
{
email,
password
}
);
alert("Login Successful");
} catch(error) {
if(error.response.status === 400) {
alert("Please enter all fields");
} else if(error.response.status === 401) {
alert("Invalid Credentials");
} else if(error.response.status === 500) {
alert("Server Error");
}
}
};
Notice how clean the frontend becomes.
No string comparisons.
No fragile logic.
Just status codes.
Real World Analogy
Imagine ordering food at a restaurant.
Instead of saying:
Order Failed
the waiter provides a reason.
Table Not Found
Item Out Of Stock
Payment Failed
Order Confirmed
Status codes work the same way.
They provide standardized communication between systems.
Where HTTPS Fits In
Everything we've discussed travels through HTTP.
However, production systems rarely use plain HTTP.
Instead:
HTTP
becomes
HTTPS
HTTPS encrypts:
Login credentials
JWT tokens
Personal information
Payment details
Without HTTPS:
Browser
↓
Internet
↓
Server
Data travels in readable form.
With HTTPS:
Browser
↓
Encrypted Data
↓
Server
Only the intended server can decrypt the information.
This is why modern applications enforce HTTPS everywhere.
Why System Designers Care About Status Codes
As applications grow:
Mobile apps consume APIs
Frontend applications consume APIs
Third-party integrations consume APIs
Microservices consume APIs
Every system relies on predictable communication.
Status codes provide that consistency.
A properly designed API is not only about returning data.
It is about returning the correct response for every possible scenario.
Key Takeaways
HTTP status codes are much more than numbers.
They are a contract between the client and the server.
A well-designed API should:
✓ Return meaningful status codes
✓ Avoid using 200 OK for failures
✓ Help the frontend understand errors
✓ Improve debugging and monitoring
✓ Create predictable communication between systems
As system complexity grows, these small design decisions become increasingly important.
The best APIs are not only functional.
They communicate clearly.
Next Article
In Part 3, we'll explore REST APIs and discover why they became the standard way modern applications communicate.



