Simona
Simona

Reputation: 379

RESTful design - sub-resource endpoints

I am designing a REST API that works with invoices and each invoice has a history of states.

There's an endpoint for getting history of states for an invoice which looks like this

/invoices/{invoice_id}/states

Is it RESTful to include this as an equivalent? Is it recommended/required?

/states/{invoice_id}

Upvotes: 1

Views: 65

Answers (1)

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57367

/invoices/{invoice_id}/states
/states/{invoice_id}

As far as REST is concerned, two different identifiers means two different resources. That doesn't mean that the server can't have two resources with synchronized representations, but it does mean that generic components won't know that these two resources are "really" the same thing.

This can matter when you are dealing with cache invalidation.

For example:

# Assume: empty cache

onRead(/invoices/{invoice_id}/states)
    # Not available locally therefore
    GET /invoices/{invoice_id}/states
    200 OK
    # Representation from server is loaded into the cache
    # Representation from server is returned to caller

onRead(/states/{invoice_id})
    # Not available locally therefore
    GET /states/{invoice_id}
    200 OK
    # Representation from server is loaded into the cache
    # Representation from server is returned to caller

onRead(/invoices/{invoice_id}/states)
    # valid representation available locally therefore
    # Representation from cache is returned to caller

onRead(/states/{invoice_id})
    # valid representation available locally therefore
    # Representation from cache is returned to caller

onChange(/invoices/{invoice_id}/states)
    # PUT, PATCH, DELETE would all be similar
    POST /states/{invoice_id}
    200 OK
    # successful request, so cached copy is invalidated

onRead(/invoices/{invoice_id}/states)
    # Not available locally therefore
    GET /invoices/{invoice_id}/states
    200 OK
    # Representation from server is loaded into the cache
    # Representation from server is returned to caller

onRead(/states/{invoice_id})
    # valid representation available locally therefore
    # Representation from cache is returned to caller

So in the end, /states/{invoice_id} is going to have a copy before the change, but /invoices/{invoice_id}/states will have a copy after the change.

If that's useful, great!

But my guess is that more often than not it is going to create problems that are unnecessarily difficult to track down. I would prefer design guidelines where all copies of the same document are accessed using the same identifier, except when there is demonstrable business value otherwise.

Upvotes: 1

Related Questions