Skip to footer content
Iron Academy Logo
Learn C#
Learn C#

Other Categories

Adding a PUT Update Endpoint in .NET Aspire on Linux

Tim Corey
8m 43s

Once an API can read and create records, the next operation is updating existing ones. A PUT endpoint replaces the entire resource with the data the caller provides, which means the request body needs every field, not just the ones that changed. That distinction between PUT (full replacement) and PATCH (partial modification) matters for how you design the input type and how callers interact with the endpoint.

In his video "Adding a PUT Update Endpoint in .NET Aspire on Linux," Tim Corey adds the update endpoint to the Tiny Ticket API, creates a dedicated update record type that includes fields the insert record did not (like ID and date completed), applies validation attributes, and tests the round trip through Swagger. The episode follows the same pattern established in previous installments but introduces a nullable DateTime field and the difference between PUT and PATCH semantics. If you are building out CRUD endpoints in a minimal API, this article covers the update side.

Creating the Update Record Type

[1:54 - 4:04] The insert record from the previous episode accepted Title, Description, and Priority. The update record needs two additional fields: the ID of the ticket being modified and the DateCompleted timestamp. Tim copies the insert record and adjusts it.

public record TicketUpdateRecord(
    [Required, Range(1, int.MaxValue)] int Id,
    [Required, MinLength(1)] string Title,
    [Required] string Description,
    DateTime? DateCompleted,
    [Range(1, 5)] int Priority
);
public record TicketUpdateRecord(
    [Required, Range(1, int.MaxValue)] int Id,
    [Required, MinLength(1)] string Title,
    [Required] string Description,
    DateTime? DateCompleted,
    [Range(1, 5)] int Priority
);

Marking Id as [Required] with a [Range(1, int.MaxValue)] constraint prevents negative values or zero from reaching the database. DateCompleted is a nullable DateTime? because a ticket that has not been resolved yet should not require a completion date. No validation attribute is needed on it since null is a valid state.

To ensure the record properties match exactly, Tim pulls the field list from the spTickets_Update stored procedure. That alignment lets Dapper map the record directly without any manual property-to-parameter wiring.

Mapping the PUT Endpoint

[4:04 - 5:44] The endpoint registration follows the established pattern. MapPut binds to the /api/tickets route, and the handler calls the stored procedure with the update record:

app.MapPut("/api/tickets", async Task<Results<NoContent, ValidationProblem>>
    (TicketUpdateRecord ticket, ISqlDataAccess sql) =>
{
    await sql.SaveDataAsync("dbo.spTickets_Update", ticket, "TicketDB");
    return TypedResults.NoContent();
});
app.MapPut("/api/tickets", async Task<Results<NoContent, ValidationProblem>>
    (TicketUpdateRecord ticket, ISqlDataAccess sql) =>
{
    await sql.SaveDataAsync("dbo.spTickets_Update", ticket, "TicketDB");
    return TypedResults.NoContent();
});

Declaring Results<NoContent, ValidationProblem> as the return type tells the framework that the endpoint produces either a 204 on success or a 400 if validation fails. The ValidationProblem variant is handled automatically by the pipeline registered in the previous episode; the handler itself only needs to return the success case.

Worth noting is how the Dapper wrapper keeps the data access concise: stored procedure name, model, connection string name. Three parameters cover the entire database call. The wrapper was written earlier in the series and continues to pay off as each new endpoint reuses it without modification.

PUT vs. PATCH: When Full Replacement Matters

[6:06 - 6:46] Before testing, Tim pauses to clarify the difference between PUT and PATCH. A PUT request replaces the entire resource: every field in the request body overwrites the corresponding database column, even if the caller did not intend to change it. A PATCH request updates only the fields included in the body.

For the Tiny Ticket project, PUT is the right choice because the front end will load the full ticket, let the user edit fields, and send the complete object back. In a production application, Tim mentions he would likely add a PATCH endpoint specifically for common single-field operations like marking a ticket as completed, where sending the entire object just to flip one date feels wasteful.

Testing the Update Through Swagger

[6:46 - 8:26] Tim launches the API and opens Swagger. Before testing the PUT, he runs the GET all endpoint to check the current state of the data. One of the test records (ID 109) has empty values for title, description, and priority from earlier testing. That becomes the target for the update.

He fills in the PUT request body with ID 109, a title of "Sample Record," a description, and a priority of 5. After executing, the response comes back as 204. Running GET all again confirms the record now has the updated values.

To verify validation, he clears the title field and executes again. The response returns a 400 with a structured error message: "The ticket title field is required." The same validation attributes from the insert endpoint carry over to the update record because they use the same annotation pattern.

Wrapping Up: CRUD Progress

[8:26 - 8:43] With the PUT endpoint complete, the Tiny Ticket API now covers three of the four CRUD operations: read (GET all and GET by ID), create (POST), and update (PUT). Each endpoint follows the same structural pattern, which makes the codebase predictable. The remaining operation is DELETE, which Tim previews as the next episode.

Conclusion

[8:38 - 8:43] Adding a PUT endpoint to a minimal API requires a dedicated update record with validation attributes, a MapPut registration with the collection URL, and a stored procedure call through the data access wrapper. The Results<NoContent, ValidationProblem> return type lets the framework handle both success and validation failure responses. Nullable fields like DateTime? pass through without requiring a validation attribute since null is a valid value for incomplete data.

Series navigation: This article is part of the C# on Linux series building the Tiny Ticket app. Previous: Adding a POST Insert Endpoint. Next episode covers the DELETE endpoint.

Example Tip: If your update stored procedure returns the modified row count, check it before returning 204. A count of zero means the ID did not match any record, and you should return a 404 instead of silently succeeding.

Watch full video on his YouTube Channel and gain more insights on building CRUD endpoints in the C# on Linux series.

Hero Worlddot related to Adding a PUT Update Endpoint in .NET Aspire on Linux
Hero Affiliate related to Adding a PUT Update Endpoint in .NET Aspire on Linux

Earn More by Sharing What You Love

Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Iron Support Team

We're online 24 hours, 5 days a week.
Chat
Email
Call Me