# PUT

PUT replaces an item with the one in the request body, its id taken from the address. The DAO updates the row and hands back the category as it was saved, and the action answers 200 OK with it, or 404 Not Found when there's no such item.

Journeyman · ASP.NET Core · REST API · Updated November 3, 2026

## After this lesson you can

- Write a PUT action that updates an item and answers 200 OK with the updated item, or 404 Not Found when it isn't there

## Key takeaways

- A `PUT` to an item's address replaces it: the id comes from the address, everything else from the body.
- `OUTPUT INSERTED` hands back the row as the `UPDATE` saved it, and Dapper's `QuerySingleOrDefault` reads it, or returns `null` when no row matched.
- Return `Ok(updated)` for `200 OK` with the updated item, or `NotFound()` for `404` when no row was updated.
- Like a `POST`, a `PUT` answers with the saved item, so the client sees exactly what the database now holds.
- `PUT` sends the whole item: leave the description out of the body, and it becomes `null`.

A client **updates** an item with a `PUT` to that item's address, sending the whole changed item as JSON in the body. The id comes from the address; everything else comes from the body. The server saves it and answers with the item as it's now stored, just as a `POST` does.

## The DAO: update, and hand back what was saved

In *POST*, `OUTPUT INSERTED.CategoryId` handed back the new id from the `INSERT`. An `UPDATE` can use `OUTPUT` too: here it hands back the whole row, as it looks after the change. Dapper's `QuerySingleOrDefault` reads that row into a `Category`:

```csharp
public Category? Update(int categoryId, Category category)
{
    string sql = """
        UPDATE Categories
        SET CategoryName = @CategoryName, Description = @Description
        OUTPUT INSERTED.CategoryId, INSERTED.CategoryName, INSERTED.Description
        WHERE CategoryId = @CategoryId;
        """;

    using SqlConnection connection = new SqlConnection(connectionString);
    return connection.QuerySingleOrDefault<Category>(sql, new { CategoryId = categoryId, category.CategoryName, category.Description });
}
```

- **`OUTPUT INSERTED.…`** lists the columns to hand back. In an `UPDATE`, `INSERTED` means *the row after the change*.
- An update of a category that doesn't exist changes **no** rows, so `OUTPUT` hands back nothing, and `QuerySingleOrDefault` returns **`null`**: the same `null` you met in `GetById`.
- The anonymous object takes the id from the parameter, not from the body: the address is the one that says which category to change.

## The action

```csharp
[HttpPut("{id}")]
public ActionResult<Category> Update(int id, Category category)
{
    Category? updated = categoryDao.Update(id, category);
    if (updated is null)
    {
        return NotFound();
    }
    return Ok(updated);
}
```

- **`[HttpPut("{id}")]`** answers `PUT /api/categories/9`.
- **`int id`** comes from the address, **`Category category`** from the body.
- **`ActionResult<Category>`**, as in *POST*: either a `Category`, or a status code result.
- **`Ok(updated)`** answers `200 OK` with the updated category in the body: *"done, and here's what it looks like now."*
- **`NotFound()`** answers `404` when no row was updated.

```http
PUT {{HostAddress}}/api/categories/9
Content-Type: application/json

{
  "categoryName": "Snacks",
  "description": "Chips, nuts, pretzels and popcorn"
}
```

```http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "categoryId": 9,
  "categoryName": "Snacks",
  "description": "Chips, nuts, pretzels and popcorn"
}
```

The same request to `/api/categories/300` answers `404 Not Found`. And because the body is a `Category`, the validation rules apply here too: a `PUT` with no `categoryName` gets the same automatic `400` as a `POST`.

## Why send the item back?

The client just sent the category, so why answer with it? Because what the database saved can differ from what was sent: the body above has no `categoryId` (the address gave it), and in a real app a table often has columns the client never sends, such as a last-changed date the database sets itself. Answering with the saved item means the client shows exactly what's stored, without a second `GET`. That's why `POST` and `PUT` both answer with the item.

(Older APIs, and older tutorials, often answer a `PUT` with `204 No Content` and an empty body. It still works, but answering `200` with the item is the common choice today.)

## PUT replaces the whole item

A `PUT` sends the **whole** item, not just what changed. Send only a `categoryName`, and the description becomes `null`: the update sets every column from the body. That's what *replace* means, and the answer shows it: its `description` is `null` too.

(There's also a `PATCH` method for changing just some fields, but it's less common and needs more code. Most APIs use `PUT`.)

## Check yourself

1 question to try on the lesson page.

## So what

PUT is update-and-show: the address says which item, the body says what it should be now, and the answer is `200` with the item as it's saved, or `404` if there was nothing to update.

## Key terms

- [PUT](https://www.ask-codey.com/glossary/put)

## Cheat sheets

- [PUT and DELETE](https://www.ask-codey.com/cheat-sheets/web-apis-with-controllers-put-cheat-sheet)

Source: https://www.ask-codey.com/lessons/web-apis-with-controllers-put
