4

Let's suppose I have the following objects in my domain:

class Warehouse
{
    int Id;
    string Address;
    Item[] Inventory;
}

class Item
{
    int Id;
    string Name;
    int Price;
    int WarehouseId;
}

I have a collection of all the warehouses and all the items. I am using a web api to provide access to them in REST style. So, when I retrieve a warehouse, I get something like this:

{
    "Id": 1,
    "Address": "123, 1st street",
    "Inventory":
    [
        { "Id" : "1", "Name": "LED TV", "Price" : "1300", "WarehouseId" : "1" },
        { "Id" : "2", "Name": "Console", "Price" : "500", "WarehouseId" : "1" },
    ]
}

What should be the result of making a PUT request to the warehouse endpoint?

Obviously, it should override the address with the provided one. But should it also override the inventory?

What if it contains a new item, not present in the collection of items? Should it create it?

What if it does not contain one of the items present before? Should it delete it?

3
  • 1
    Keep It simple. Use PUT to add or replace the whole resource as it has been requested. Use PATCH for deltas. Otherwhise you will end up implementing a sort of diff-comparator. Your business is unlikely to be reinventing the wheel
    – Laiv
    Sep 26, 2019 at 19:14
  • 2
    Why not? What does PUT implies? Does PUT has nothing to do with your implementation details? As soon as PUT does what semantically is supposed to do is fine. Is it idempotent? Good. Is it unsafe? Good. What else do you expect?
    – Laiv
    Sep 26, 2019 at 20:25
  • Instead of making Warehouse a complex object, you can make Warehouse to have links to Inventory objects. That way it'll be clear that you update them separately.
    – imel96
    Sep 26, 2019 at 23:33

3 Answers 3

5

I would separate the warehouse from the items:

GET api/warehouse 
Gets a list of warehouses

PUT api/warehouse/{warehouseid} 
Updates some meta information on that warehouse (address etc)

GET api/warehouse/{warehouseid}/inventory 
Gets the inventory of the warehouse (you can supply some search parameters to narrow

GET api/warehouse/warehouseid}/inventory/{itemid} 
Gets a particular item, details, how many in warehouse, etc.

DELETE api/warehouse/warehouseid}/inventory/{itemid} 
Delete a particular item from that inventory or at least decrements the qty that is stored in the warehouse by one.

And so forth.

3

If the Inventory is truly part of your Warehouse resource, then the PUT should override the state of the Inventory array. How you store the data in the back-end, e.g. an RDBMS or NoSQL DB, should not affect the semantics of your REST endpoints. The resources your consumers care about are the ones in your API, not the underlying data structures, so you should make the semantics consistent with the resources you're exposing. However you make that work on the backend, whether a PUT ends up doing inserts in a SQL table or whatever, is irrelevant as long as the PUT semantics are consistent for the resource as defined.

That said, in this particular case, you may be better served by having an API like @JonRaynor suggested, in which the inventory is a sub-resource of the warehouse. This would allow you to separate the updating of the warehouse metadata like the ID and the Address from the transactional data like the inventory, which will probably make life easier for both you (when implementing the back-end data layer), and your consumers (when trying to understand how to use your API).

1

The easiest way to avoid the complexity of updates is to expose endpoints that are use case/command oriented as opposed to CRUD oriented. For example you might have a POST warehouse/{id}/addressChange endpoint that only takes the data related to changing an address as opposed to doing a wholesale update. If you're doing DDD this would be directly reflected in public methods on your warehouse object. The inventory items should only be changed via your warehouse object, if the warehouse is understood to be the aggregate root and it necessary to maintain transactional (as opposed to eventual) consistency between the inventory items and the warehouse.

1
  • There was a suggestion to edit this and add a note that this endpoint violates REST design. That's not necessarily the case. While REST endpoints aren't supposed to function like a RPCs. There is nothing that prevents /addressChange from acting as a reified action (a command), in other words, a noun resource. Dec 21, 2021 at 15:10

Your Answer

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