backend / rest apis / 04_put_vs_patch.md

PUT vs PATCH: Understanding the Difference

3 interview angles 4 min read source

PUT vs PATCH: Understanding the Difference

Basic Definition

Method Purpose
PUT Complete replacement of a resource
PATCH Partial modification of a resource

Detailed Comparison

PUT

  • Complete replacement: Replaces the entire resource with the provided payload
  • Idempotent: Multiple identical PUT requests will have the same effect as a single request
  • Requires full representation: The client must send the complete updated representation
  • Creates or updates: If the resource doesn’t exist, PUT can create it (if the server allows)

PATCH

  • Partial update: Modifies only specified parts of the resource
  • Not necessarily idempotent: Depends on the implementation and payload format
  • Sends only changes: The client only sends the changes to be applied
  • More efficient for partial updates: Reduces bandwidth when only updating a few fields

Example with a User Resource

Consider a user resource with the following JSON representation:

{
    "id": 123,
    "name": "John Doe",
    "email": "john@example.com",
    "age": 30,
    "address": {
        "street": "123 Main St",
        "city": "Anytown"
    }
}

PUT Update

To update just the email address using PUT, you must send the ENTIRE resource:

PUT /users/123 HTTP/1.1
Content-Type: application/json

{
    "id": 123,
    "name": "John Doe",
    "email": "john.doe@newemail.com",
    "age": 30,
    "address": {
        "street": "123 Main St",
        "city": "Anytown"
    }
}

PATCH Update

With PATCH, you only send the specific changes:

PATCH /users/123 HTTP/1.1
Content-Type: application/json

{
    "email": "john.doe@newemail.com"
}

When to Use Each

Use PUT when:

  • Replacing a resource entirely
  • Creating a resource with a known identifier
  • The operation needs to be idempotent
  • The complete state of the resource is known

Use PATCH when:

  • Making partial updates to a resource
  • Conserving bandwidth is important
  • Only a few fields need modification
  • Working with large resources where sending the complete representation is inefficient

Implementation in Python (API Frameworks)

Using Flask

from flask import Flask, request, jsonify

app = Flask(__name__)
users = {
    '123': {
        "id": "123",
        "name": "John Doe",
        "email": "john@example.com",
        "age": 30
    }
}

@app.route('/users/<user_id>', methods=['PUT'])
def update_user_put(user_id):
    if user_id not in users:
        # PUT can create a new resource
        users[user_id] = request.json
        return jsonify(users[user_id]), 201
    else:
        # PUT replaces the entire resource
        users[user_id] = request.json
        return jsonify(users[user_id]), 200

@app.route('/users/<user_id>', methods=['PATCH'])
def update_user_patch(user_id):
    if user_id not in users:
        return jsonify({"error": "User not found"}), 404
    
    # PATCH updates only specified fields
    for key, value in request.json.items():
        users[user_id][key] = value
    
    return jsonify(users[user_id]), 200

Using Django REST Framework

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class UserDetail(APIView):
    def put(self, request, user_id):
        # PUT replaces the entire user
        user = get_user(user_id)
        if not user:
            # Create new user with specified ID
            serializer = UserSerializer(data=request.data)
        else:
            # Replace existing user completely
            serializer = UserSerializer(user, data=request.data)
            
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    def patch(self, request, user_id):
        # PATCH updates only specified fields
        user = get_user(user_id)
        if not user:
            return Response({"error": "User not found"}, status=status.HTTP_404_NOT_FOUND)
            
        # Partial update - only update fields that were provided
        serializer = UserSerializer(user, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Best Practices

  1. Always make PUT idempotent: Ensure that multiple identical PUT requests have the same effect as a single request
  2. Use PATCH with specific formats: Consider using formats like JSON Patch (RFC 6902) for complex updates
  3. Validate complete resources with PUT: Ensure the entire resource is valid when using PUT
  4. Handle missing fields in PATCH: Define clear semantics for what happens when fields are missing in a PATCH
  5. Document the behavior: Make it clear to API consumers what each method does

Conclusion

Understanding the difference between PUT and PATCH is essential for designing RESTful APIs. PUT is for complete replacement of resources, while PATCH is for partial updates. Choose the appropriate method based on your specific needs, considering factors like idempotence, bandwidth efficiency, and resource state management.

Interview angle

  • “PUT or PATCH?” - PUT replaces the resource entirely and is idempotent; PATCH applies a partial modification and is not necessarily idempotent. Sending a partial body to PUT is the common misuse, and it silently clears the omitted fields.
  • “How do you model a partial update in Python?” - a Pydantic model with all fields optional, then model_dump(exclude_unset=True) so you distinguish “not provided” from “explicitly set to null”. That distinction is where most patch bugs live.
  • “How do you avoid a lost update?” - optimistic concurrency with ETag plus If-Match, or a version field. Two clients patching different fields concurrently will otherwise overwrite each other.