0

HTTP status codes — the ones that matter in API design

Beginner5 min read·lv-10-002
interview

Concept

HTTP status codes communicate the result of a request to the client. Using the right status code is essential for API design — clients (browsers, mobile apps, API consumers) use status codes to decide how to handle responses.

Success (2xx):

  • 200 OK: Standard success. GET responses, PUT/PATCH success.
  • 201 Created: Resource successfully created (POST). Include Location header with the new resource URL.
  • 202 Accepted: Request received, but processing is asynchronous (job dispatched).
  • 204 No Content: Success but no response body. DELETE operations, PATCH that don't return the updated resource.

Client errors (4xx):

  • 400 Bad Request: Malformed request syntax, missing required fields. Different from 422.
  • 401 Unauthorized: Authentication required (not authenticated). Despite the name, it's about authentication, not authorization.
  • 403 Forbidden: Authenticated but not authorized. Permission denied.
  • 404 Not Found: Resource doesn't exist.
  • 405 Method Not Allowed: HTTP method not allowed for this endpoint.
  • 409 Conflict: Conflict with current state (duplicate entry, version mismatch).
  • 422 Unprocessable Entity: Validation failed — request was well-formed but semantically invalid.
  • 429 Too Many Requests: Rate limit exceeded. Include Retry-After header.

Server errors (5xx):

  • 500 Internal Server Error: Unhandled exception.
  • 502 Bad Gateway: Upstream service failed.
  • 503 Service Unavailable: Maintenance mode or overload. Include Retry-After.

Code Example

php
<?php
// Status code constants (from Symfony HttpFoundation)
use Symfony\Component\HttpFoundation\Response;
Response::HTTP_OK;                   // 200
Response::HTTP_CREATED;              // 201
Response::HTTP_NO_CONTENT;           // 204
Response::HTTP_BAD_REQUEST;          // 400
Response::HTTP_UNAUTHORIZED;         // 401
Response::HTTP_FORBIDDEN;            // 403
Response::HTTP_NOT_FOUND;            // 404
Response::HTTP_UNPROCESSABLE_ENTITY; // 422
Response::HTTP_TOO_MANY_REQUESTS;    // 429

// Practical usage in controllers
class OrderController extends Controller
{
    // 201 Created — resource created
    public function store(Request $request): JsonResponse
    {
        $order = Order::create($request->validated());
        return response()->json(
            new OrderResource($order),
            201,
            ['Location' => route('orders.show', $order)]
        );
    }

    // 204 No Content — deleted successfully
    public function destroy(Order $order): \Illuminate\Http\Response
    {
        $order->delete();
        return response()->noContent(); // 204
    }

    // 202 Accepted — async processing
    public function exportRequest(Request $request): JsonResponse
    {
        ExportOrdersJob::dispatch(auth()->id());
        return response()->json(['message' => 'Export queued'], 202);
    }

    // 409 Conflict — duplicate entry
    public function store(Request $request): JsonResponse
    {
        if (Order::where('idempotency_key', $request->idempotency_key)->exists()) {
            return response()->json(['message' => 'Duplicate request'], 409);
        }
        // ...
    }
}

// abort() helper — throws HttpException → exception handler renders it
abort(404);                                     // 404
abort(403, 'You cannot access this resource'); // 403
abort_if(!$user->isAdmin(), 403);               // conditional abort
abort_unless($user->owns($order), 403);         // inverse condition

// HTTP exceptions in exception handler
// ValidationException → 422 (automatic)
// ModelNotFoundException → 404 (via renderable in Handler)
// AuthenticationException → 401 (via unauthenticated() in Handler)
// AuthorizationException → 403 (via Handler)