When a client changes only one part of the resource, you can optimize the entire update process by allowing the client to send only the modified part to the server and thereby saving the bandwidth and server resources. RFC 5789 proposes a solution for this use case via a new HTTP method, PATCH
. We have already covered the theoretical concepts behind the PATCH method in Chapter 8, RESTful API Design Guidelines, under the Implementing partial update section. In this section, we will see how to actually implement the PATCH
method in your JAX-RS application.
JAX-RS allows you to define annotations to represent any custom or non-standard HTTP methods by annotating them with javax.ws.rs.HttpMethod
. We will use this feature to define the @PATCH
annotation, which can be used later to designate resource class methods for responding to HTTP PATCH method calls. Following is an example:
import javax.ws.rs.HttpMethod; import javax.ws.rs.NameBinding; //Other imports are removed for brevity @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @HttpMethod("PATCH") @Documented @NameBinding public @interface PATCH { }
As we discussed at the beginning of this section, a PATCH
request sends only the modified fields to the server, along with the type of operations performed on each modified field.
The following example illustrates a PATCH request for a department resource:
PATCH /departments/10 HTTP/1.1 [ { "op": "replace", "path": "/managerId", "value": 300 }, { "op": "add", "path": "/locationId", "value": 1200 } ]
JAX-RS does not support a PATCH
method by default. However, this does not prevent you from supporting PATCH
in your application. Following are the steps that you need to take:
The resource method that you define should have logic to read the partial changes sent by the client.
The next step is to construct the resource entity representation out of the partial representation.
The last step is to save the changes.
The following code snippet demonstrates how you can implement a partial update for the Department
resource. This example uses the json-patch
library available in GitHub (https://github.com/fge/json-patch) to read the partial updates present in the request payload and build an entity out of it:
//Other imports are removed for brevity import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.fge.jsonpatch.JsonPatch; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; @Path("departments") public class DepartmentResource{ @PATCH @Path("{id}") @Consumes({"application/json-patch+json"}) public Response patch(@PathParam("id") Short id, String deptUpdatesInJSON) { // get the dept from DB Department dept = findDepartmentEntityFromDB(id); try { if (dept != null) { //The mapper provides //functionality for converting between Java objects //and matching JSON constructs. ObjectMapper mapper = new ObjectMapper(); // convert JSON string to a Java class //JsonPatch is here https://github.com/fge/json-patch JsonPatch patch = mapper.readValue(deptUpdatesInJSON, JsonPatch.class); // convert Dept from DB to a JSON object JsonNode deptJson = mapper.valueToTree(dept); // apply patch on latest Dept read from DB JsonNode patched = patch.apply(deptJson); // convert the patched object to Dept to store dept = mapper.readValue(patched.toString(), Department.class); // save the patched department object saveEntity(dept); }else{ //Dept obj for the patch sent by client is //not found in DB return Response.serverError(). status(Status.NOT_FOUND).build(); } } catch (Exception ex) { throw new WebApplicationException(ex); } return Response.ok().build(); } //Other methods are removed for brevity }
Tip
The complete source code for this example is available in the Packt website. You can download the example from the Packt website link that we mentioned at the beginning of this book, in the Preface section. In the downloaded source code, see the rest-appendix-patch-demo
project to get a feel of the sample PATCH
implementation that we discussed in this section.
You can use REST API testing tools, such as the Postman REST client or the SoapUI, to quickly test the PATCH
implementation that we built. The sample PATCH API call may look like the following:
PATCH /rest-appendix-patch-demo/webresources/departments/10 HTTP/1.1 Host: localhost:8080 Content-Type: application/json-patch+json Cache-Control: no-cache [ { "op": "replace", "path": "/departmentName", "value": "IT"}, { "op": "add", "path": "/locationId", "value": 1200 } ]