Updating Resources in Spring Boot 3: PUT vs PATCH Explained

In this post, we’ll explore the differences between the PUT and PATCH methods in the context of updating resources in Spring Boot 3.

· Prerequisites
· Overview
∘ Understanding PUT and PATCH
∘ When to Use PUT vs PATCH
· Implementing PUT and PATCH in Spring Boot 3
∘ Setting Up a Spring Boot Project
∘ PUT in Spring Boot
∘ PATCH in Spring Boot
· Test the REST APIs
∘ PUT (Full update)
∘ PATCH (Partial update)
· Conclusion
· References


Prerequisites

This is the list of all the prerequisites:

  • Spring Boot 3
  • Maven 3.6.3 or later
  • Java 21
  • IntelliJ IDEA, Visual Studio Code, or another IDE
  • Postman / insomnia or any other API testing tool.

Overview

Spring Boot 3 introduced several new features (native images, virtual threads, and Jakarta EE 9 namespaces), but it did not alter the semantics of HTTP. When it comes to updating resources, the same old question still haunts REST endpoints: Should it be PUT or PATCH?

In this post, we’ll clarify the differences between PUT and PATCH, show how to implement both using Spring Boot 3, and guide you through choosing the right method for your use case.

Understanding PUT and PATCH

Before diving into the implementation details, let’s clarify what PUT and PATCH are:

  • PUT: The PUT method is used to update a resource by replacing it entirely with the new representation provided in the request. Any omitted fields are typically reset or removed. A successful PUT of a given representation would suggest that a subsequent GET on that same
    target resource will result in an equivalent representation being
    sent in a 200 (OK) response. However, there is no guarantee that such a state change will be observable, since the target resource might be acted upon by other user agents in parallel, or might be subject to dynamic processing by the origin server, before any subsequent GET is received. A successful response only implies that the user agent’s intent was achieved at the time of its processing by the origin server.
  • PATCH: The PATCH method, on the other hand, is used to apply partial modifications to a resource. This means you only need to send the fields that you want to update, rather than the entire resource. PATCH is particularly useful for large resources where only a few fields need to be changed.

The difference between the PUT and PATCH requests is reflected in the way the server processes the enclosed entity to modify the resource identified by the Request-URI. In a PUT request, the enclosed entity is considered to be a modified version of the resource stored on the origin server, and the client is requesting that the stored version be replaced. With PATCH, however, the enclosed entity contains a set of instructions describing how a resource currently residing on the origin server should be modified to produce a new version. The PATCH method affects the resource identified by the Request-URI, and it also MAY have side effects on other resources; i.e., new resources may be created, or existing ones modified, by the application of a PATCH.

https://datatracker.ietf.org/doc/html/rfc5789

When to Use PUT vs PATCH

Choosing between PUT and PATCH depends on the specific use case:

Use PUT (Full Update) when:

  • You want to replace the entire resource.
  • You are sure that the client has a complete representation of the resource.
  • You want to ensure that the resource is in a consistent state after the update.

Use PATCH (Partial Update) when:

  • You only need to update specific fields of a resource.
  • You want to minimize the amount of data sent over the network.
  • You are working with large resources where only a few fields change frequently.

Implementing PUT and PATCH in Spring Boot 3

Now that we understand the differences, let’s look at how to implement both methods in a Spring Boot 3 Rest aPI.

Setting Up a Spring Boot Project

We’ll start by creating a simple Spring Boot project from start.spring.io, with the following dependencies: Spring Web, Lombok, H2 Database, Spring Data JPA, and Validation.

The domain model we’ll use

@Entity
@Table(name = "book")
public class Book implements Serializable {

@Serial
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;

@Column(name = "title", nullable = false, length = 100)
private String title;

@NotNull
@Column(name = "isbn")
private String isbn;

@Column(name = "description")
private String description;

@Column(name = "page")
private Integer page;

@Column(name = "price")
private BigDecimal price;
}

PUT in Spring Boot

Full Update DTO (PUT)

public record BookDto(
Long id,
String title,
String isbn,
String description,
Integer page,
BigDecimal price
) {}

PUT Endpoint

@RestController
@Validated
@RequestMapping("/api/books")
public class BookController {

private final BookRepository repo;

public BookController(BookRepository repo) {
this.repo = repo;
}

@PutMapping("/{id}")
public ResponseEntity<BookDto> replaceBook(@PathVariable Long id, @Valid @RequestBody BookDto dto) {

Book entity = repo.findById(id)
.orElseThrow(() -> new EntityNotFoundException("Book not found"));

// Replace all fields
entity.setTitle(dto.title());
entity.setIsbn(dto.isbn());
entity.setDescription(dto.description());
entity.setPage(dto.page());
entity.setPrice(dto.price());

var book = repo.save(entity);
return ResponseEntity.ok(new BookDto(book.getId(), book.getTitle(), book.getIsbn(), book.getDescription(), book.getPage(), book.getPrice()));
}

}

PATCH in Spring Boot

Option 1: Using Map for Dynamic Updates

@PatchMapping("/{id}")
public ResponseEntity<BookDto> patchBook(@PathVariable Long id, @RequestBody Map<String, Object> updates) {

    Book entity = repo.findById(id)
            .orElseThrow(() -> new EntityNotFoundException("Book not found"));

    // Replace existing fields
    updates.forEach((key, value) -> {
        switch (key) {
            case "title" -> entity.setTitle((String) value);
            case "isbn" -> entity.setIsbn((String) value);
            case "description" -> entity.setDescription((String) value);
            case "page" -> entity.setPage((Integer) value);
            case "price" -> entity.setPrice(new BigDecimal(value.toString()));
        }
    });

    var book = repo.save(entity);

    return ResponseEntity.ok(new BookDto(book.getId(), book.getTitle(), book.getIsbn(), book.getDescription(), book.getPage(), book.getPrice()));
}

Instead of replacing the whole object, this method selectively updates fields provided in the request body. This is done using a Map<String, Object>, which allows clients to specify only the fields they want to change.

updates.forEach((key, value) -> {
switch (key) {
case "title" -> entity.setTitle((String) value);
...
}
});

This approach allows flexibility for the frontend or API client — they don’t need to send the entire object.

Option 2: Using Record with Optional fields for Type-Safety

Partial Update DTO (PATCH) — Optional Fields

public record BookPatchDTO(
Optional<String> title,
Optional<String> isbn,
Optional<String> description,
Optional<Integer> page,
Optional<BigDecimal> price
) {}

Endpoint

@PatchMapping("/{id}")
public ResponseEntity<BookDto> partialBook(@PathVariable Long id, @RequestBody BookPatchDTO dto) {

    Book entity = repo.findById(id)
            .orElseThrow(() -> new EntityNotFoundException("Book not found"));

    // Replace existing fields
    dto.title().ifPresent(entity::setTitle);
    dto.isbn().ifPresent(entity::setIsbn);
    dto.description().ifPresent(entity::setDescription);
    dto.page().ifPresent(entity::setPage);
    dto.price().ifPresent(entity::setPrice);

    var book = repo.save(entity);

    return ResponseEntity.ok(new BookDto(book.getId(), book.getTitle(), book.getIsbn(), book.getDescription(), book.getPage(), book.getPrice()));
}

Test the REST APIs

Now we are all done with our code. We can run our application and test it.

Initial data values in the database

PUT (Full update)

  • All fields must be provided; missing fields will be set to null.
  • Idempotent — Repeated requests produce the same result.

PATCH (Partial update)

Option 1: Using Map for Dynamic Updates

PATCH http://localhost:8080/api/books/2

Only the page and price fields are updated; other fields remain unchanged.

Option 2: Using Record with Optional fields for Type-Safety

PATCH http://localhost:8080/api/books/3
  • Only isbn and title are updated.

Conclusion

Well done !!.

Understanding the difference between PUT and PATCH is crucial for designing clean, RESTful APIs. While PUT is ideal for full resource replacement, PATCH provides a more efficient way to update only the fields that change. In this post, we explored both approaches in Spring Boot 3, saw a practical partial update implementation, and highlighted when each method should be used.

The complete source code is available on GitHub.

Support me through GitHub Sponsors.

Thank you for reading!! See you in the next post.

References

👉 Link to Medium blog

Related Posts