KAHIBARO
Discord Login Register

PUT Requests

Understanding PUT Requests

In REST APIs, PUT is one of the core HTTP methods you will use to update data on the server. It has a very specific meaning and behavior that is different from POST, PATCH, and others. In this chapter, you will learn what makes PUT special and how to use it correctly when designing and consuming REST APIs.


What Is a PUT Request?

A PUT request is an HTTP request that tells the server:

“Store the resource I am sending at this exact URL. If it already exists, replace it. If it does not exist, you may create it.”

So, PUT is usually used to create or completely replace a resource at a known URL.

A typical PUT request looks like this:

http
PUT /users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
  "id": 123,
  "name": "Alice",
  "email": "alice@example.com",
  "is_active": true
}

The client here is saying: “The user with ID 123 should now have exactly this data. Store it at /users/123.”

Compare that with POST to /users, which usually means “create a new user, you (the server) choose the ID and URL.”


PUT vs POST vs PATCH

Understanding where PUT fits requires comparing it with POST and PATCH.

Conceptual comparison

MethodTypical UseTarget URL patternBody containsIdempotent
POSTCreate a new resource under a collection/usersNew resource data (often partial)No
PUTCreate or fully replace a resource at a URL/users/123Full resource representationYes
PATCHPartially update part of a resource/users/123Only fields to changeNot guaranteed

Key ideas:

Important rule:
Use PUT when you want to fully replace the resource representation at a known URL, and the operation should be idempotent (same request repeated has the same final effect).
Use PATCH when you want to partially update fields.
Use POST to create new resources under a collection or to trigger non-idempotent actions.


Idempotency of PUT

Idempotent means:

Performing the same operation multiple times in a row results in the same final state as doing it once.

This does not mean the responses have to be exactly the same each time, but the resource on the server must end up in the same state.

Example of idempotent PUT

Imagine a user resource:

json
{
  "id": 1,
  "name": "Alice",
  "email": "alice@old.com",
  "is_active": true
}

You send:

http
PUT /users/1
Content-Type: application/json
{
  "id": 1,
  "name": "Alice Wonderland",
  "email": "alice@new.com",
  "is_active": false
}

The resource after each operation is:

json
{
  "id": 1,
  "name": "Alice Wonderland",
  "email": "alice@new.com",
  "is_active": false
}

No matter how many times you repeat this PUT, the final state is the same, so the operation is idempotent.

Why idempotency matters

Idempotency is very useful for:

Compare with POST:

Full Replacement vs Partial Updates

The usual interpretation of PUT is full replacement.

Full replacement semantics

With full replacement:

Example: current resource

json
{
  "id": 10,
  "name": "My Task",
  "description": "Do something",
  "is_done": false
}

You send:

http
PUT /tasks/10
Content-Type: application/json
{
  "id": 10,
  "name": "My Task (updated)"
}

Possible server interpretation with full replacement:

json
{
  "id": 10,
  "name": "My Task (updated)",
  "description": null,
  "is_done": null
}

If you want to keep other fields unchanged, you would include them in the body:

http
PUT /tasks/10
Content-Type: application/json
{
  "id": 10,
  "name": "My Task (updated)",
  "description": "Do something",
  "is_done": false
}

Important rule:
With PUT, the request body should usually contain the full representation of the resource.
If you only want to modify some fields and keep others unchanged, use PATCH.

Some real APIs relax this rule and treat PUT more like PATCH, but that is confusing and not fully RESTful. For clean API design, keep the meanings different and clear.


Typical URL Design for PUT

PUT almost always targets a single, specific resource.

Common patterns

Resource typeCollection URLSingle resource URLExample PUT
Users/users/users/{user_id}PUT /users/123
Products/products/products/{sku}PUT /products/ABC-123
Tasks/tasks/tasks/{task_id}PUT /tasks/42

In many APIs:

The important part: PUT uses a URL that identifies exactly one resource. The client is responsible for knowing that URL and often even the ID.


Creating vs Updating with PUT

Should you use PUT only for updates, or also for creating resources?

Two valid interpretations

There are two common approaches:

  1. Update only
    • If the resource does not exist, respond with 404 Not Found.
    • PUT is only allowed when the resource already exists.
  2. Create or replace (upsert-like)
    • If the resource exists, replace it with the provided representation.
    • If the resource does not exist, create it at that URL.

Both patterns are seen in real APIs. You should decide and document the behavior in your API.

Example: PUT as “update only”

Request:

http
PUT /users/123
Content-Type: application/json
{
  "id": 123,
  "name": "Alice",
  "email": "alice@example.com"
}

Example: PUT as “create or replace”

Same request:

http
PUT /users/123
Content-Type: application/json
{
  "id": 123,
  "name": "Alice",
  "email": "alice@example.com"
}

Server response for creation might be:

http
HTTP/1.1 201 Created
Location: /users/123
Content-Type: application/json
{
  "id": 123,
  "name": "Alice",
  "email": "alice@example.com"
}

Important rule:
If you allow PUT to create resources, return 201 Created for new resources, and include a Location header with the resource URL.
If you use PUT only for updates and the resource does not exist, return 404 Not Found.


Common Response Codes for PUT

PUT typically uses a small set of HTTP status codes.

Status codeWhen to use for PUT
200 OKResource was updated or created and response body is returned
201 CreatedResource was newly created at this URL
204 No ContentResource was updated and no body is returned
400 Bad RequestInvalid data or malformed request body
401 UnauthorizedAuthentication required or invalid
403 ForbiddenAuthenticated but not allowed to update this resource
404 Not FoundResource to update does not exist
409 ConflictConflicting state, such as version mismatch or duplicate
422 Unprocessable EntityValidation failed for request data

Many APIs choose between 200 and 204 for successful updates:

Example:

http
PUT /tasks/10
Content-Type: application/json
{
  "id": 10,
  "title": "Buy milk",
  "is_done": true
}

Response with updated resource:

http
HTTP/1.1 200 OK
Content-Type: application/json
{
  "id": 10,
  "title": "Buy milk",
  "is_done": true
}

Or minimal response:

http
HTTP/1.1 204 No Content

PUT Request Examples

Example 1: Updating a user profile

URL: /users/42

Current server data:

json
{
  "id": 42,
  "username": "jdoe",
  "full_name": "John Doe",
  "email": "john@example.com",
  "bio": "Hello world"
}

Client wants to change the full_name and bio. Since this is PUT, the client sends full data:

http
PUT /users/42
Content-Type: application/json
{
  "id": 42,
  "username": "jdoe",
  "full_name": "John M. Doe",
  "email": "john@example.com",
  "bio": "Backend developer"
}

Possible response:

http
HTTP/1.1 200 OK
Content-Type: application/json
{
  "id": 42,
  "username": "jdoe",
  "full_name": "John M. Doe",
  "email": "john@example.com",
  "bio": "Backend developer"
}

Example 2: Creating or replacing a product

URL: /products/ABC-123

Client:

http
PUT /products/ABC-123
Content-Type: application/json
{
  "sku": "ABC-123",
  "name": "Blue T-Shirt",
  "price": 19.99,
  "in_stock": true
}

If the product did not exist before:

http
HTTP/1.1 201 Created
Location: /products/ABC-123
Content-Type: application/json
{
  "sku": "ABC-123",
  "name": "Blue T-Shirt",
  "price": 19.99,
  "in_stock": true
}

If it existed:

http
HTTP/1.1 200 OK
Content-Type: application/json
{
  "sku": "ABC-123",
  "name": "Blue T-Shirt",
  "price": 19.99,
  "in_stock": true
}

Example 3: Handling invalid PUT data

Request:

http
PUT /users/50
Content-Type: application/json
{
  "id": 50,
  "email": "not-an-email",
  "username": ""
}

Server validation:

Response:

http
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
  "detail": [
    {"field": "email", "message": "Invalid email format"},
    {"field": "username", "message": "Username is required"}
  ]
}

Designing PUT in Your REST API

When you design your own API, decide and document how PUT works in your system.

Checklist for PUT design

  1. Define the URL pattern
    • Usually: /resources/{id}.
      Example: /tasks/{task_id}.
  2. Decide if PUT can create
    • Update only (404 if not found), or
    • Create or replace (201 when created, 200 or 204 when updated).
  3. Use full representation in the body
    • Require all mandatory fields.
    • Either set missing optional fields to null or keep the previous value, but be consistent and document it.
  4. Use consistent status codes
    • 200 or 204 for success, 201 when created.
    • 400/422 for validation errors.
    • 404 when resource is not found.
    • 401/403 when not authenticated or not authorized.
  5. Keep PUT idempotent
    • Repeating the same PUT should not change the resource again.
    • Avoid side effects like sending emails or creating new resources as part of PUT.
  6. Return updated resource when helpful
    • Especially useful when the server modifies data (for example sets updated_at, normalizes strings, adds links).

Example: Task API with PUT

Resource: Task

json
{
  "id": 1,
  "title": "Buy milk",
  "is_done": false,
  "due_date": "2026-09-01T10:00:00Z"
}

API decisions:

Client request:

http
PUT /tasks/1
Content-Type: application/json
{
  "id": 1,
  "title": "Buy oat milk",
  "is_done": true,
  "due_date": "2026-09-01T10:00:00Z"
}

Server response:

http
HTTP/1.1 200 OK
Content-Type: application/json
{
  "id": 1,
  "title": "Buy oat milk",
  "is_done": true,
  "due_date": "2026-09-01T10:00:00Z",
  "updated_at": "2026-08-27T12:34:56Z"
}

If task 1 did not exist:

http
HTTP/1.1 404 Not Found
Content-Type: application/json
{
  "detail": "Task not found"
}

Practical Tips and Common Pitfalls

1. Do not mix PUT semantics with PATCH

Avoid using PUT for partial updates. Some APIs do this:

http
PUT /users/1
Content-Type: application/json
{
  "full_name": "New Name"
}

and then only update full_name. This blurs the line between PUT and PATCH and can confuse clients.

Better:

2. Consider resource versioning for concurrent updates

Two clients might try to update the same resource at nearly the same time, for example:

Without protection, the last write overwrites previous changes. Many APIs use a version field or ETag and 409 Conflict to detect this, which is part of optimistic locking, but the details belong to more advanced topics.

3. Validate IDs in body vs URL

If your resource has an id field and the URL also includes {id}, you should ensure they match.

Example:

http
PUT /users/123
Content-Type: application/json
{
  "id": 999,
  "name": "Alice"
}

You can:

What you choose is up to your API design, but be consistent and document it.

4. Handling missing or optional fields

Decide what happens when a client omits optional fields in PUT:

For strict full replacement semantics, missing fields should usually be treated as removed. But this can surprise clients, so many APIs document clearly which fields must always be sent and which ones are optional and preserved.


Summary

Understanding and using PUT correctly is a key step toward building clean, predictable REST APIs.

Views: 6

Comments

Please login to add a comment.

Don't have an account? Register now!