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.

Frequently asked questions

What happens if you leave a field out of a PUT request?+

Because PUT replaces the entire resource, the server may clear the missing field or reset it to its default value. Sending a PUT without every field can accidentally erase data, which is why partial updates are usually done with PATCH instead.

Is PATCH idempotent?+

Not by spec. PUT is idempotent, but a PATCH that supports operations such as increment changes the result each time it runs. Most APIs keep PATCH to simple field replacement, which makes it practically idempotent, but retrying a PATCH after a timeout can still apply a change twice.

Should I use PATCH or PUT for updates?+

Default to PATCH, because partial updates are more common, payloads are smaller, and clients do not need to fetch the full resource first. Use PUT when you are genuinely replacing the whole resource or need strict idempotency for retries. Either way, the spec should state which is allowed.

Related lessons

Read more on the blog