beginner·6 min read·Updated May 1, 2026

PATCH vs PUT: The Difference That Trips Everyone

PUT replaces. PATCH modifies. Here's the subtle but critical difference — and why it matters for testing.

The one-sentence difference

PUT replaces the entire resource. PATCH modifies only the fields you send.

Everything else — idempotency, when to use which, bugs that arise from mixing them up — flows from this one idea.

Why it matters

Imagine a user record:

{
  "id": "abc-123",
  "name": "Charlie",
  "email": "charlie@example.com",
  "role": "user",
  "age": 30
}

You want to promote Charlie to admin. With PATCH, you send:

PATCH /users/abc-123
{ "role": "admin" }

The server applies only that field. Everything else stays the same.

With PUT, you send:

PUT /users/abc-123
{ "name": "Charlie", "email": "charlie@example.com", "role": "admin" }

Note what's missing: age. Because PUT replaces the entire resource, the server may clear age (or reset it to the default). You've accidentally erased data by forgetting to include it.

Walk through it live

Create a user with an age field:

POST/api/v1/users
Create a user so we have something to update.
curl -X POST 'https://demo.totalshiftleft.ai/api/v1/users' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","email":"charlie@example.com","role":"user","age":30}'

Save the id. Now PATCH just the role:

PATCH/api/v1/users/{id}
PATCH only the role. Replace {id}.
curl -X PATCH 'https://demo.totalshiftleft.ai/api/v1/users/{id}' \
  -H 'Content-Type: application/json' \
  -d '{"role":"admin"}'

Response shows: role is now admin, name/email/age are unchanged.

Now let's PUT without the age:

PUT/api/v1/users/{id}
PUT replaces. Notice age is missing — it may be cleared.
curl -X PUT 'https://demo.totalshiftleft.ai/api/v1/users/{id}' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","email":"charlie@example.com","role":"admin"}'

Response shows: age is gone (or reset). That's the hazard of PUT in partial-update flows.

Idempotency

  • PUT is idempotent. Sending the same PUT request 10 times produces the same final state. PUT /users/123 { ... } with the same body always leaves the resource looking the same way.
  • PATCH is often NOT idempotent. If PATCH supports operations like { "op": "increment", "path": "/count" }, running it twice increments twice. Most APIs keep PATCH simple (just field replacement), making it practically idempotent — but it's not guaranteed by spec.

This matters for retries: you can safely retry a PUT after a network timeout, but retrying a PATCH might apply the change twice if the server processed your original request but the response was lost.

Which should you use?

Default to PATCH for 2026 APIs. Reasons:

  1. Partial updates are more common than full replacements.
  2. PATCH payloads are smaller (less bandwidth, less parsing).
  3. Clients don't need to re-fetch the full resource before updating one field.

Use PUT when:

  • You're genuinely replacing the whole resource (e.g., overwriting a configuration blob).
  • You need strict idempotency guarantees for retry safety.
  • You're following a specific REST purist convention your team has agreed on.

Some APIs support both, letting clients pick. Some only support one. Both approaches are defensible — the critical thing is that the spec says clearly which is allowed.

Bugs to watch for

1. "PUT that behaves like PATCH." Server accepts a partial body on PUT and merges it silently. This violates the spec and causes exactly the "missing data" bug above — except worse, because you thought PUT was safe.

2. "PATCH that requires full body." Server rejects PATCH with partial body, demanding every field. Also a spec violation, but less dangerous.

3. Immutable fields. Some fields shouldn't be updatable (id, created_at). The server should either ignore them on PATCH/PUT or 400 if they're sent. Test both behaviors.

4. Unknown fields. If PATCH ignores unknown fields silently, typos like roel get lost. Test that PATCH with { "unknown_field": "x" } returns 400 or a clear warning.

What to assert in tests

For every PATCH-able resource:

  • Send only one field, assert that field changed, assert others unchanged.
  • Send two fields, assert both changed.
  • Send an immutable field (id), assert 400 or ignored.
  • Send a typo field (unknwn), assert 400 (if strict) or no crash.
  • Send an empty body {}, assert 400 or 200 with no change (document which).

For every PUT-able resource:

  • Send the full required body, assert 200 and all fields correct.
  • Omit a required field, assert 400.
  • Send extra unknown fields, document behavior.

What's next

You've nailed the trickiest REST subtlety. Next: RESTful best practices — the conventions that make REST APIs predictable, testable, and a joy to use.

Related lessons

Read more on the blog