Skip to main content

Command Palette

Search for a command to run...

Web Architecture Fundamentals

Updated
5 min read
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.


Master System Design with Java

Part 2 of 2

A complete beginner-to-advanced journey into System Design using Java. Learn how modern applications are built and scaled through practical examples, real-world architectures, design patterns, microservices, databases, caching, messaging systems, cloud deployment, and hands-on implementation with Spring Boot. Perfect for software engineers, Java developers, and aspiring system architects.

Start from the beginning

Web Architecture Fundamentals

Part 1: Understanding Client-Server Architecture — The Foundation of Modern Applications Whenever we open a web application, watch a video online, send a message, or purchase a product, we interact wi