My question is, should I return a HTTP response code of 400 Bad Request, 404 Not Found or 410 Gone when a parent resource, or higher, does not exist / has been deleted?
Looking for some guidance around how to handle a missing links in a RESTful resource tree, as I've read quite a bit but not a whole lot of personal experience.
Say we have the resources structure below:
/users/{userId}/accounts/{accoundId}/logs/{logId}
One user can have many accounts, which in turn can have many orders, which in turn can have many logs. Logs only exist against a single account, and accounts only exist against a single user. Logs cannot exist without an account, and accounts cannot exist without a user.
My problem comes when I try to resolve the below:
/users/123/accounts/321/logs - POST
/users/123/accounts/321/logs/987 - GET
/users/123/accounts/321/logs/987 - PUT
/users/123/accounts/321/logs/987 - DELETE
/users/123/accounts/321/logs/987 - PATCH
/users/123/accounts/321/logs/987 - HEAD
But this resource does not, or no longer, exists:
/users/123/accounts/321
or this resource does not, or no longer, exists:
/users/123
I could argue it's a 400 Bad Request, which as per RFC7231:
6.5.1. 400 Bad Request
The 400 (Bad Request) status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).
Would be by definition true, except in the scenario that a cache hadn't expired, meaning the application had yet to re-traverse the hierarchy, and another application had deleted the parent resource. By providing a relevant oplock, the client will have proven that as per its last knowledge, it was making a semantically correct request.
I instinctively would have lent towards 404 Not Found or 410 Gone even if this wasn't a cache thing, as the cause of the failure was literally a missing/unavailable resource. However as per the spec RFC7231:
6.5.4. 404 Not Found
The 404 (Not Found) status code indicates that the origin server did not find a current representation for the target resource or is not willing to disclose that one exists. A 404 status code does not indicate whether this lack of representation is temporary or
permanent; the 410 (Gone) status code is preferred over 404 if the
origin server knows, presumably through some configurable means, that the condition is likely to be permanent.
or
6.5.9. 410 Gone
The 410 (Gone) status code indicates that access to the target
resource is no longer available at the origin server and that this
condition is likely to be permanent.
These would appear to give the lie to that instinct.
Does anyone have any experience in dealing with this, or similar, scenarios and good approaches? I feel I should go with what feels right and what I'd expect to see if consuming this service, rather than the letter of the text.
A thing to keep in mind: the HTTP response, including the metadata, which includes the status code, are specific to the requested resource, not some implicit hierarchy of things.
Which is to say
GET /users/{userId}/accounts/{accoundId}/logs/{logId}
requests: a current selected representation for the target resource. See RFC 7231; in particular, the request doesn't ask about representations of any of these:
/users/{userId}/accounts/{accoundId}/logs/
/users/{userId}/accounts/{accoundId}/
/users/{userId}
...
It's much simpler than that - can I have what I asked for? And the origin server provides a response, which may be a current representation, or it may be a message explaining that no such representation is available.
The fact that no representation of /users/{userId}/accounts/{accoundId}/logs/{logId}
is available because /users/{userId}/accounts/{accoundId}
does not exist is an implementation detail.
404 is normally what you would want to use as a status code to announce that no current representation of the target resource is available. Any explanation for why this is the case would normally go into the message body. For instance, you might describe it using problem details.
The server is not obligated to send an expired representation of a resource just because it happens to have one lying around (again, the cache is an implementation detail).
410 is almost the same thing; it's effectively an advisory that the client can mark its bookmarks as deprecated.
400 is a perfectly reasonable way to punt if you can't find a status code that better fits the semantics of the message in the payload. But I wouldn't use if for this case where 404
does actually fit your needs.