REST - Updating partial data

1.3k Views Asked by At

I am currently programming a REST service and a website that mostly uses this REST service.

Model:

public class User {
    private String realname;
    private String username;
    private String emailAddress;
    private String password;
    private Role role;
    ..
}

View:

One form to update

  • realname
  • email address
  • username

Another form to update the role

And a third form to change the password

.

Focussing on the first view, which pattern would be a good practice?

PUT /user/{userId}

imho not because the form contains only partial data (not role, not password). So it cannot send a whole user object.

PATCH /user/{userId}

may be ok. Is a good way to implement it like:

1) read current user entity

2)

if(source.getRealname() != null) // Check if field was set (partial update)
dest.setRealname(source.getRealname());
.. for all available fields

3) save dest

POST /user/{userId}/generalInformation

as summary for realname, email, username

.

Thank you!

2

There are 2 best solutions below

1
On

One problem with this approach is that user cannot nullify optional fields since code is not applying the value if (input is empty and value) is null. This might be ok for password or other required entity field but for example if you have an optional Note field then the user cannot "clean" the field.

Also, if you are using a plain FORM you cannot use PATCH method, only GET or POST.

If you are using Ajax you might be interested in JSON Merge Patch (easier) and/or JavaScript Object Notation (JSON) Patch (most complete); for an overview of the problems that one can find in partial updates and in using PATCH see also this page.

A point is that a form can only send empty or filled value, while a JSON object property can have three states: value (update), null (set null) and no-property (ignore).

An implementation I used with success is ZJSONPATCH

0
On

Focussing on the first view, which pattern would be a good practice?

My suggestion starts from a simple idea: how would you do this as web pages in HTML?

You probably start from a page that offers a view of the user, with hyperlinks like "Update profile", "Update role", "Change password". Clicking on update profile would load an html form, maybe with a bunch of default values already filled in. The operator would make changes, then submit the form, which would send a message to an endpoint that knows how to decode the message body and update the model.

The first two steps are "safe" -- the operator isn't proposing any changes. In the last step, the operator is proposing a change, so safe methods would not be appropriate.

HTML, as a hypermedia format, is limited to two methods (GET, POST), so we might see the browser do something like

GET  /user/:id
GET  /forms/updateGeneralInformation?:id
POST /updates/generalInformation/:id

There are lots of different spellings you can use, depending on how to prefer to organize your resources. The browser doesn't care, because it's just following links.

You have that same flexibility in your API. The first trick in the kit should always be "can I solve this with a new resource?".

Ian S Robinson observed: specialization and innovation depend on an open set. If you restrict yourself to a closed vocabulary of HTTP methods, then the open set you need to innovate needs to lie elsewhere: the RESTful approach is to use an open set of resources.

Update of a profile really does sound like an operation that should be idempotent, so you'd like to use PUT if you can. Is there anything wrong with:

GET /user/:id/generalInformation
PUT /user/:id/generalInformation

It's a write, it's idempotent, it's a complete replacement of the generalInformation resource, so the HTTP spec is happy.

Yes, changing the current representation of multiple resources with a single request is valid HTTP. In fact, this is one of the approaches described by RFC 7231

Partial content updates are possible by targeting a separately identified resource with state that overlaps a portion of the larger resource

If you don't like supporting multiple views of a resource and supporting PUT on each, you can apply the same heuristic ("add more resources") by introducing a command queue to handle changes to the underlying model.

GET /user/:id/generalInformation
PUT /changeRequests/:uuid

Up to you whether you want to represent all change requests as entries in the same collection, or having specialized collections of change requests for subsets of operations. Tomato, tomahto.