1. Library
  2. Http and the Web
  3. Methods

Updated 10 hours ago

Every developer eventually asks the same question: If I send an update without including every field, what happens to the fields I left out?

The answer depends entirely on whether you use PUT or PATCH.

PUT says "this is the whole truth now." Whatever you send replaces what exists. Fields you omit are gone—or reset to defaults. You're not updating; you're overwriting.

PATCH says "change these things, leave the rest alone." Only the fields you include are modified. Everything else stays exactly as it was.

This distinction sounds simple, but it determines how you structure requests, how servers process them, and whether a forgotten field causes a production incident.

PUT: Complete Replacement

PUT sends the entire resource representation. The server doesn't merge—it replaces.

Say you have a user profile:

{
  "id": 123,
  "name": "John Doe",
  "email": "john@example.com",
  "bio": "Software developer",
  "location": "New York"
}

To update the email with PUT:

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

{
  "id": 123,
  "name": "John Doe",
  "email": "john.doe@example.com",
  "bio": "Software developer",
  "location": "New York"
}

You must include everything—even the fields that didn't change. If you sent only:

{
  "id": 123,
  "email": "john.doe@example.com"
}

The name, bio, and location fields would be wiped out. That's not a bug. That's PUT's semantic contract: whatever you send is the complete new state.

PATCH: Surgical Modification

PATCH sends only what needs to change:

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

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

The server modifies the email and leaves everything else untouched. The result:

{
  "id": 123,
  "name": "John Doe",
  "email": "john.doe@example.com",
  "bio": "Software developer",
  "location": "New York"
}

PATCH expresses intent more precisely: "change this specific thing." The request is smaller, and there's no risk of accidentally erasing fields you meant to keep.

The Idempotency Question

PUT is always idempotent. Send the same PUT request ten times and you get the same result—the resource ends up in the exact state you specified. This makes PUT safe to retry if a network request fails.

PATCH is trickier. Setting a field to a specific value is idempotent:

{"email": "new@example.com"}

Apply this a hundred times; the email is still new@example.com.

But relative changes are not idempotent:

{"balance": {"increment": 10}}

Apply this twice and you've added 20, not 10. Whether PATCH is idempotent depends on what you're patching and how.

When to Use Each

Use PUT when:

  • You're replacing the entire resource anyway
  • The client has loaded the complete current state and is sending back modifications
  • You need guaranteed idempotency
  • Configuration objects where partial state doesn't make sense

Use PATCH when:

  • Clients typically modify one or two fields at a time
  • The resource is large and sending it all wastes bandwidth
  • Multiple clients might update different fields simultaneously
  • You want the request to express exactly what's changing

PATCH Formats

Unlike PUT, which uses the resource's standard representation, PATCH can describe changes in different ways.

JSON Merge Patch (RFC 7396) is the simplest: send an object with the fields you want to update. Set a field to null to delete it. Omit fields to leave them unchanged.

{
  "email": "new@example.com",
  "bio": null
}

This updates the email and removes the bio. Simple, but it can't distinguish between "set this field to null" and "delete this field"—both use null.

JSON Patch (RFC 6902) uses an array of explicit operations:

[
  {"op": "replace", "path": "/email", "value": "new@example.com"},
  {"op": "remove", "path": "/bio"},
  {"op": "add", "path": "/tags/-", "value": "verified"}
]

More powerful—you can append to arrays, move values, even test conditions before applying changes—but more complex to implement.

Most APIs use JSON Merge Patch for its simplicity. Choose JSON Patch when you need precise control over arrays or conditional updates.

Handling Concurrent Modifications

PATCH creates a race condition risk. Two clients read the same resource, both send patches, and the second one overwrites the first's changes without knowing they existed.

The solution is optimistic locking with ETags:

PATCH /users/123 HTTP/1.1
If-Match: "abc123"
Content-Type: application/json

{"email": "new@example.com"}

The server applies the patch only if the resource's ETag still matches. If someone else modified it first, you get 412 Precondition Failed and can reload, merge, and retry.

PUT has the same issue, but it's often less problematic because you're sending the complete state. You might overwrite someone's changes, but at least you're not leaving the resource in an unexpected partial state.

Creating Resources

PUT can create resources at client-specified URLs:

PUT /users/johndoe HTTP/1.1

{"name": "John Doe", "email": "john@example.com"}

If /users/johndoe doesn't exist, it's created. If it exists, it's replaced. This idempotent creation is useful when clients can generate meaningful identifiers—usernames, SKUs, UUIDs.

PATCH typically only modifies existing resources. Patching something that doesn't exist returns 404.

Response Codes

Both PUT and PATCH commonly return:

  • 200 OK: Success, response includes the updated resource
  • 204 No Content: Success, no response body (client infers the new state)
  • 400 Bad Request: Malformed request
  • 404 Not Found: Resource doesn't exist
  • 409 Conflict: Update conflicts with current state
  • 412 Precondition Failed: ETag mismatch

PUT may also return 201 Created when creating a new resource.

Returning the updated resource in the response saves a subsequent GET request. Returning 204 saves bandwidth. Either approach works; just be consistent.

Frequently Asked Questions About PUT vs. PATCH

Was this page helpful?

😔
🤨
😃