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
- Always make PUT idempotent: Ensure that multiple identical PUT requests have the same effect as a single request
- Use PATCH with specific formats: Consider using formats like JSON Patch (RFC 6902) for complex updates
- Validate complete resources with PUT: Ensure the entire resource is valid when using PUT
- Handle missing fields in PATCH: Define clear semantics for what happens when fields are missing in a PATCH
- 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.