Http Methods and Status Codes

The HTTP specification includes a collection of methods that are used to interact with server-side resources. There are also HTTP method properties to consider, including whether a HTTP request is safe, idempotent, or cacheable.

Method
Safe
Idempotent
Cacheable

GET

✔️

✔️

✔️

HEAD

✔️

✔️

✔️

OPTIONS

✔️

✔️

TRACE

✔️

✔️

DELETE

✔️

❌ , invalidates cache

PUT

✔️

❌, invalidates cache

POST

Sometimes

PATCH

Sometimes

CONNECT

No

Method properties

HTTP requests have different properties and there are some important commonalities. Request methods can be safe, idempotent, and/or cacheable.

Safe

HTTP method that is safe will not have any effect on the state of the server, it can be thought of as one that is read-only.

The safe HTTP methods are: GET, HEAD, OPTIONS, and TRACE.

However, in practice, this is often not realistic because there is no guarantee that the server does not generate side effects because of the HTTP request.

For example, if each HTTP GET request causes a publicly visible “visitor counter” to change, then technically, the state of the server has changed. Importantly, the HTTP GET operation is still considered safe because the client did not request the side effect.

Idempotent

An idempotent method has an identical outcome if called multiple times. This means that after the initial HTTP request is sent, successive HTTP requests are of no consequence.

The idempotent HTTP methods are: GET, HEAD, OPTIONS, TRACE, DELETE, and PUT.

All safe methods are idempotent, but DELETE and PUT are not safe because they intentionally affect the state of the server.

Cacheable

A cacheable method generates a HTTP response that can be cached.

HTTP GET and HEAD requests are cacheable.

HEAD

  • Description: Retrieves only headers (no response body). Useful for checking resource availability.

  • Characteristics: Idempotent , Safe, Cacheable

  • Annotation: @RequestMapping(method = RequestMethod.HEAD)

  • Status Code: 200 , 404, 400

OPTIONS

  • Description: It is used to inquire about what operations are available for the specified resource or server in general.

  • Characteristics: Idempotent , Safe

  • Annotation: @RequestMapping(method = RequestMethod.OPTIONS)

  • Status Code: 204 , 400, 500

TRACE

  • Description: Performs a diagnostic loop-back test to see how a request travels through intermediaries.

  • Characteristics: Idempotent , Safe

  • Annotation: @RequestMapping(method = RequestMethod.TRACE)

  • Status Code: 200 , 405

GET

  • Description: Used to retrieve data from a server. Does not modify the resource.

  • Characteristics: Idempotent, Cacheable, Safe (can alter state)

  • Annotation: @GetMapping

  • Status Code: 200, 204 , 400, 404, 500

POST

  • Description: Sends data to the server, often to create a resource or trigger processing.

  • Characteristics:

    • Idempotent: No (repeated requests can create duplicate resources).

    • Safe: No (alters server state).

    • Cacheable: No (response is typically not cached).

  • Annotation: @PostMapping

  • Status Code: 201, 200, 207 [multi] , 400, 409, 500

PUT

  • Description: Replaces the entire target resource with the request payload.

  • Characteristics:

    • Idempotent: Yes (subsequent requests with the same payload have the same effect).

    • Safe: No.

    • Cacheable: No.

  • Annotation: @PutMapping

  • Status Code: 201, 200, 204 , 400, 404, 500

PATCH

  • Description: Partially updates a resource, allowing modification of specific fields.

  • Characteristics:

    • Idempotent: No (behavior depends on server implementation).

    • Safe: No.

    • Cacheable: No.

  • Annotation: @PatchMapping

  • Status Code: 200, 204 , 400, 404, 500

DELETE

  • Description: Deletes a resource identified by the URL.

  • Characteristics: Idempotent

  • Annotation: @DeleteMapping

  • Status Code: 200, 204 , 400, 404, 500

CONNECT

  • Description: Establishes a network connection tunnel, often used for HTTPS requests.

  • Characteristics: None

Bulk Creation [Post]

 // Bulk creation with partial success handling
    @PostMapping("/batch")
    public ResponseEntity<BulkOperationResult<ProductDTO>> createProducts(
     @RequestBody List<ProductDTO> products) {
        BulkOperationResult<ProductDTO> result = productService.bulkCreate(products);
        
        if (result.hasPartialFailures()) {
            return ResponseEntity
                .status(HttpStatus.MULTI_STATUS)  // Returns 207 Multi-Status
                .body(result);
        }
        return ResponseEntity.status(HttpStatus.CREATED).body(result);
    }

Conditional Update [Put]

// Conditional update using ETag
    @PutMapping("/{customerId}")
    public ResponseEntity<CustomerDTO> updateCustomerConditional(
            @PathVariable String customerId,@RequestBody CustomerDTO customer,
            @RequestHeader("If-Match") String etag) {
        
        try {
            CustomerDTO updated = customerService.updateWithETag(customer, etag);
            return ResponseEntity.ok()
                .eTag("\"" + updated.getVersion() + "\"")
                .body(updated);
        } catch (OptimisticLockException e) {
            return ResponseEntity
                .status(HttpStatus.PRECONDITION_FAILED)  // Returns 412 Precondition Failed
                .build();
        }
    }

Partial Update [Patch]

// Partial update using JSON Patch
    @PatchMapping(value = "/{orderId}", 
                 consumes = "application/json-patch+json")
    public ResponseEntity<OrderDTO> patchOrder(
            @PathVariable String orderId,
            @RequestBody JsonPatch patch) {
        
        try {
            OrderDTO patched = orderService.applyPatch(orderId, patch);
            return ResponseEntity.ok()  // Returns 200 OK
                .body(patched);
        } catch (JsonPatchException e) {
            return ResponseEntity
                .status(HttpStatus.UNPROCESSABLE_ENTITY)  // Returns 422 Unprocessable Entity
                .build();
        }
    }
    
    // Status update - specific business operation
    @PatchMapping("/{orderId}/status")
    public ResponseEntity<OrderDTO> updateOrderStatus(
            @PathVariable String orderId,
            @Valid @RequestBody OrderStatusUpdate statusUpdate) {
        
        try {
            OrderDTO updated = orderService.updateStatus(orderId, statusUpdate);
            return ResponseEntity.ok(updated);
        } catch (IllegalStateException e) {
            return ResponseEntity
                .status(HttpStatus.CONFLICT)  // Returns 409 Conflict
                .build();
        }
    }

Bulk Delete [Delete]

 // Single resource deletion
    @DeleteMapping("/{productId}")
    public ResponseEntity<Void> deleteProduct(@PathVariable String productId) {
        productService.delete(productId);
        return ResponseEntity
            .noContent()  // Returns 204 No Content
            .build();
    }
    
    // Bulk deletion with preconditions
    @DeleteMapping
    public ResponseEntity<Void> deleteProducts(
            @RequestParam List<String> ids,
            @RequestHeader("If-Match") String etag) {
        
        try {
            productService.bulkDelete(ids, etag);
            return ResponseEntity
                .noContent()
                .build();
        } catch (OptimisticLockException e) {
            return ResponseEntity
                .status(HttpStatus.PRECONDITION_FAILED)
                .build();
        }
    }

How to handle deletion of same resource multiple times ?

  1. For Pure REST APIs:

    • Return 204 for all successful DELETE operations

    • This follows REST idempotency principles

    • Simplest to implement and use

@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable String id) {
    // Even if resource doesn't exist, return 204
    // The end state is the same - resource doesn't exist
    productService.delete(id);
    return ResponseEntity.noContent().build();
}
  1. For Business-Critical Systems:

    • Consider using 410 GONE for already deleted resources

    • Maintain deletion audit trail

    • More complex but provides better tracking

@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable String id) {
    DeletionResult result = productService.delete(id);
    
    return switch (result) {
        case DELETED -> ResponseEntity.noContent().build();        // 204
        case ALREADY_DELETED -> ResponseEntity.status(410).build(); // 410 GONE
        case NOT_FOUND -> ResponseEntity.notFound().build();       // 404
    };
}
  1. For Event-Driven Systems:

    • Use soft deletes

    • Publish deletion events

    • Maintain complete state history

@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable String id) {
    try {
        DeletionEvent event = productService.softDelete(id);
        eventPublisher.publish(event);
        
        return ResponseEntity.noContent().build();
    } catch (ResourceAlreadyDeletedException e) {
        // Still return 204 for idempotency
        return ResponseEntity.noContent().build();
    }
}

Status Codes

Here’s a table listing HTTP status codes commonly used in API development.

Code

Name

Description

100

Continue

The client should continue the request or ignore the response if the request is already finished.

101

Switching Protocols

The server is switching protocols as requested by the client.

102

Processing (WebDAV)

The server has received and is processing the request but no response is available yet.

200

OK

The request was successful, and the server returned the requested data.

201

Created

The request was successful, and a new resource was created.

202

Accepted

The request has been accepted for processing, but the processing is not complete.

204

No Content

The server successfully processed the request, but no content is returned.

301

Moved Permanently

The resource has been moved to a new URL permanently.

302

Found (Temporary Redirect)

The resource has temporarily moved to a different URL.

304

Not Modified

The requested resource has not been modified since the last access.

400

Bad Request

The server could not understand the request due to invalid syntax or parameters.

401

Unauthorized

Authentication is required to access the resource.

403

Forbidden

The client does not have permission to access the resource.

404

Not Found

The server could not find the requested resource.

405

Method Not Allowed

The HTTP method is not allowed for the requested resource.

409

Conflict

The request conflicts with the current state of the resource.

422

Unprocessable Entity

The server understands the content type but was unable to process the contained instructions.

429

Too Many Requests

The client has sent too many requests in a given time period.

500

Internal Server Error

The server encountered an unexpected condition that prevented it from fulfilling the request.

501

Not Implemented

The server does not support the functionality required to fulfill the request.

503

Service Unavailable

The server is not ready to handle the request, often due to maintenance or overloading.

504

Gateway Timeout

The server did not receive a timely response from an upstream server or external service.

511

Network Authentication Required

The client needs to authenticate to gain network access.

Status Code Best Practices

Here's a summary of when to use specific status codes:

  1. Success Codes (2xx):

    • 200 OK: Successful GET, PUT, PATCH

    • 201 Created: Successful POST creating new resource

    • 204 No Content: Successful DELETE

    • 207 Multi-Status: Batch operations with partial success

  2. Client Error Codes (4xx):

    • 400 Bad Request: Invalid syntax

    • 401 Unauthorized: Authentication required

    • 403 Forbidden: Authenticated but not authorized

    • 404 Not Found: Resource doesn't exist

    • 409 Conflict: Business rule violation

    • 412 Precondition Failed: ETag mismatch

    • 422 Unprocessable Entity: Validation errors

  3. Server Error Codes (5xx):

    • 500 Internal Server Error: Unexpected server errors

    • 503 Service Unavailable: Service temporarily unavailable

Last updated