3

What is the proper way of implementing an object property update using ServiceStack (message-oriented RESTful web service)?

If the client needs to update Foo.bar and only has Foo's id.

Should I accept a FooBarUpdate request object that includes the Foo id and Foo.bar value? This would mean that I have to create separate request for each property update, or at least for those that I expect to be updated (or a combination of them). This also doesn't seem to adhere to RESTful standards.

Alternatively, if I accept a Foo request object that includes the whole Foo with the updated properties, the client would first need to retrieve Foo and then the service would check which properties were updated. This seems like an unnecessary overhead.

Lastly, if I accept a Foo request but with only the id and the modified property value (Foo.bar), the Foo object is incomplete and according to other posts, this can lead to confusion where someone assumes it's a full object.

1 Answer 1

6

How to handle RESTful updates:

If Bar is an individual property on Foo then there is no reason you couldn't just have an UpdateFooRequest that contained the updated Bar.

Like this:

[Route("/foo/{Id}", "POST")]
public class UpdateFooRequest
{
    public int Id { get; set; }
    public Bar Bar { get; set; }
}

However if Bar was a collection such that Foo is:

public class Foo
{
    public int Id { get; set; }
    public Bar[] Bar { get; set; }
}

Then the more restful way to update a Bar item on Foo is to reference the instance of bar through the Foo route. But you need to know the bar's id, (or index in the collection relative to it's Foo).

[Route("/foo/{FooId}/bar/{Id}", "POST")]
public class UpdateFooBarRequest : Bar
{
    public int FooId { get; set; }
}

Handling partial updates at the server

To address these concerns:

Alternatively, if I accept a Foo request object that includes the whole Foo with the updated properties, the client would first need to retrieve Foo and then the service would check which properties were updated. This seems like an unnecessary overhead.

The only time you need to be concerned with a whole Foo is when Foo is being created, and it will have it's own route. So there should be nothing to worry about here.

[Route("/foo", "POST")]
public class CreateFooRequest
{
    ...
}

When Foo is being updated you will post to an existing Foo route like this /foo/123:

[Route("/foo/{Id}", "POST")]
public class UpdateFooRequest
{
    public int Id { get; set; }
    ...
}

Ultimately your service should be able to instruct the ORM to update just the fields that have been included in the request with the changes. So if the request has only a couple of changes, then the ORM will handle that.

As an example ORMLite update is simply:

db.Update<Foo>(updateFooRequest);

This will create a SQL UPDATE on the row identified by Id using only the fields that are found in the request;

If a whole Foo is sent then allow the ORM to treat it as changing many fields, the record will just be overwritten. You could do a lookup of the record, as you suggested, before the update and determine the changed fields, but as you say this is an unnecessary overhead.

As a rule if your update method handles taking partial changes, then sending a whole Foo shouldn't be problematic, but ideally a client should just send the changes.

Lastly, if I accept a Foo request but with only the id and the modified property value (Foo.bar), the Foo object is incomplete and according to other posts, this can lead to confusion where someone assumes it's a full object.

The last line where someone assumes it's a full object, is slightly worrying. The update route should only be used for updates, you shouldn't want to use the full object here. The update method should expect the request Foo to be partial, and is handled as I explained above.


I think you should probably read up on ORMLite it shows some great examples of how partial updates can be applied to existing data. While it's not necessary your ORM of choice, the basic concepts apply in other ORMs and I think this illustrates them well.


To address updating an item Bar in a collection on Foo by name:

It's rather unconventional to do that. A disadvantage of using a value that has meaning, such as Name, is that you won't be able to easily change this property - as it is an identifier. If it is static then it's fine.

So Foo would look something like this:

public class Foo
{
    public int Id { get; set; }
    public Bar[] Bar { get; set; }
}

Let's assume Bar

public class Bar
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Baz { get; set; }
    public bool IsEnabled { get; set; }
}

If I am using Name and not Id of Bar to update, I can't change the Name property because then I don't know which Bar to update. I would now need a whole new route to handle changing the Bar's name.

If you do decide to use it as the identifier in the collection you could do:

[Route("/foo/{FooId}/bar/{Name}", "POST")]
public class UpdateFooBarRequest : Bar
{
    public int FooId { get; set; }
}

So to update Foo 123 with an updated Baz = 13 and IsEnabled = false on Bar named "Bob"

POST /foo/123/bar/Bob
{
    Baz: 13,
    IsEnabled: false
}

In your action method you would need to update only items matching the name "Bob" having Foo 123. In ORMLite it would look something like this:

db.Update(updateBarRequest, p => p.FooId == updateBarRequest.FooId && p.Name == updateBarRequest.Name);

But doing it this way means you can't do:

[Route("/foo/{FooId}/bar/{Id}", "POST")] and [Route("/foo/{FooId}/bar/{Name}", "POST")] because the router won't know if you are passing an Id or a Name so then you have to do unconventional REST like [Route("/foo/{FooId}/bar/byName-{Name}", "POST")].

I hope this helps.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the detailed answer! How would I handle a case where Foo has a collection of Bar's but I only have the "name" property of the Bar and not its ID? What would be an appropriate route in that case?
@MT-WSN You're welcome. I have updated to include that scenario.
@Scott I guess in case of updates PUT should be used instead of POST according to REST manifest

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.