Retrofit 2 — How to Update Objects on the Server (PUT vs. PATCH)

In the Retrofit getting started tutorial you've learned how to create an Android app that connects to the public GitHub API. In following tutorials you've learned how to use OAuth to make requests as an authorized GitHub user. Finally, you've learned how to create new objects.

In this tutorial you'll learn how to update existing objects on the server. Most APIs utilize either a PUT or PATCH HTTP method for this task. If you're not familiar with HTTP requests yet, we recommend our tutorial on network basics.

If you're ready to go, let's start!

Retrofit Series Overview

Update Objects via an API

You already know how to create new objects: you send an object as the request payload to the server with a POST method. For example, you could send the following request, authenticated with an auth token, to create a new GitHub Gist:

POST /gists

{
  "description": "the description for this gist",
  "public": true,
  "files": {
    "file1.txt": {
      "content": "String file contents"
    }
  }
}

This would create a new public GitHub Gist object with a description and a file file1.txt. Of course, the payload completely depends on your API and the object you're creating. Nevertheless, most APIs expect a POST request for creating objects.

So how do you update an existing object? There are two different ways: PUT vs PATCH. We'll start with the more common one: PUT.

PUT Requests

PUT requests are used by the majority of the APIs. The request is actually very similar to creating a new object. You'll also send the full object to the server — even if you only change a single property. Furthermore, because you're sending a full object again, you need to tell the API that you want to update an existing object. Thus, the HTTP method changes from POST to PUT. Finally, you need to specify which object you want to update by stating the object ID.

For example, if you would want to update our GitHub Gist object (ID: 12343) from above, you would change the HTTP methods and update the payload to the change object.

PUT /gists/12343

{
  "description": "the NEW description for this gist",
  "public": true,
  "files": {
    "file1.txt": {
      "content": "String file contents"
    }
  }
}

In most cases this is how to update objects via an API. You use the PUT HTTP method, you specify the exact object (e.g., via an ID) and send the new object along. This new object replaces the existing one.

The API interface description with Retrofit would be straightforward:

public class Gist {  
    // all Gist properties  
      // ...
}

public interface GistService {  
    @PUT("gists/{id}")
    Call<ResponseBody> updateGist(@Path("id") String id, @Body Gist gist);
}

You've to imagine PUT less as updating and more as a replacing of existing objects. The downside is that for large objects this can become quite inefficient.

Let's image your GitHub gist has 1000 files and you only want to change the content of a single file, or just the description. Too bad, with PUT you would have to send a giant request with all files to the server. GitHub Gists are a primary example where PUT is at its limits. That's why GitHub actually doesn't offer PUT for Gists. Instead, you've to use PATCH requests, which we'll look at next.

PATCH Requests

PATCH requests have the exact same goal as PUT requests. You want to update an existing object on the server. However, the approach between PUT and PATCH is different. PUT requests replace the entire object. On the other hand, PATCH requests only send the changed properties, and ignore everything that stays the same.

For example, if we would update the description of our Gist, we would send this request with a much smaller payload:

PATCH /gists/12343

{
  "description": "the NEW description for this gist"
}

Please note how the HTTP method URL still contains the ID of the object. Consequently, the Retrofit implementation would be almost identical:

public class Gist {  
    // all Gist properties  
      // ...
}

public interface GistService {  
    @PATCH("gists/{id}")
    Call<ResponseBody> updateGist(@Path("id") String id, @Body Gist gist);
}

The biggest difference for clients is that they've to make sure they're aware of the changed properties and creates a matching Java object, and subsequent payload.

In our Gist example we would need to create a new Gist object where we don't send anything besides the updated description property. Be careful, you often cannot send properties as null, which have not changed! Null properties are understood as "delete this". For example, if we would want to change the description of our Gist and delete the existing file file1.txt, this request would do it:

PATCH /gists/12343

{
  "description": "the NEW description for this gist",
  "files": {
    "file1.txt": null
  }
}

In summary, make sure Retrofit isn't automatically sending null properties in your payload to avoid negative surprises.

As you can see just with the small Gist example, PATCH requests are usually more efficient on the network. However, the logic for clients becomes a little more complicated, because they've to be aware which and how properties changed. Of course, in doubt, the client could just send a full object as a PATCH request and essentially mimic a PUT request. That would work just fine, but lose the network efficiency effect of PATCH.

GitHub opted to only offer the PATCH method with their Gist API. However, with other APIs you might find either, or just one of the methods.

Summary

In this tutorial you've learned how to update objects on the server. You've seen two different approaches: PUT to replace existing objects, and PATCH to selectively change object properties. Either way has its advantages and, unfortunately, as the app developer you often don't have a choice. Nevertheless, with this tutorial at hand you know can handle both without any issues!

Do you have further questions on this topic or about Retrofit in general? Just let us know on Twitter @futurestud_io or leave a comment below.

Enjoy coding & make it rock!

Explore the Library

Find interesting tutorials and solutions for your problems.

Miscellaneous