It's an account
It's an account

Reputation: 173

REST HTTP response code for missing parent resource

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.

Upvotes: 14

Views: 5612

Answers (3)

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57214

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.

Upvotes: 15

Quality Catalyst
Quality Catalyst

Reputation: 6795

NO: Rule out the 400 Bad Request as all the requests, which you have mentioned, are valid.

NO: To add some spice: I've seen a 409 Conflict being returned in similar cases. It appears inappropriate in your situation though as you clearly indicated that a resource is missing.

YES: A 404 Not Found is the most appropriate response if your resource does not exist. It doesn't matter if it ever existed or not. The 404 indicates a "sorry, resource unavailable". I would add an error message to be precise about which resource is missing so that your API consumers can handle the situation better.

MAYBE: A 410 Gone is a specific case of a 404. It should be raised if the resource is unavailable and it existed before and it will (practically) never exist again. So no matter how often and when you try to get the resource, you'll never get it again, but in the past, you may have been able to get it. Same thing here: if you decided on a 410 consider adding a precise error message.

On a personal note:

  • I have never seen a practical useful situation of a 410 Gone hence I avoid it and recommend my teams not to use it unless they can come up with a real good reason. It appears a little academic. In most cases, a 404 and 410 will be dealt with identically by your API consumers. They most often don't mind/care. I can only see rare edge cases to make valuable sense of a differentiation.
  • A DELETE often doesn't delete a resource. They get inactivated (made unavailable), but they are still available technically. If there's any chance that a resource may become available again, such as via a new feature called "return all recently deleted resources", a 410 would be already misleading.
  • A 410 also states that that resource did exist in the past indeed. From a data securities point of view, you could argue that information should kept internal and should not be exposed. In these cases, a 410 becomes a general no-no for your API.

Upvotes: 5

ChrisRibe
ChrisRibe

Reputation: 168

410 Gone

If /users/123/accounts/321/logs/987 no longer exists, then 410 Gone is the most appropriate response. The fact that it was deleted as a result of /users/123 being deleted is irrelevant to the request for /users/123/accounts/321/logs/987.

400 Bad Request would be a more appropriate response for something like a request to /users/123/accounts/321/lergs/987.

Upvotes: -2

Related Questions