I know the 404 vs 204 debate has been beaten to death, and I understand the argument for using 404 when there is no record in the table corresponding to a REST endpoint request, but it feels like there should be some way of differentiating between "This endpoint is malformed" and "there is no record in the table." For example if I have an endpoint like this:
https://mycloudfront.cloudfront.com/api/my-table/{userId}
Is there a recommended way of configuring error handling on the backend to differentiate between "no resource found because there is no entry for userId" and "no resource found because there is no table named my-table" or "no resource found because there is no cloudfront distribution named mycloudfront"?
I ask, because it would be nice on the frontend to inform the end user whether or not their request did not produce the desired result because they have no data in the table (in which case I would display a message encouraging them to take an action that would generate data) or because something went wrong (in which case I would display an error message).
CodePudding user response:
it would be nice on the frontend to inform the end user whether or not their request did not produce the desired result because they have no data in the table
That's what the response body is for.
Except when responding to a HEAD request, the server SHOULD send a representation containing an explanation of the error situation, and whether it is a temporary or permanent condition. RFC 9110.
Status codes are metadata in the transfer-of-documents-over-a-network domain (Webber, 2011) - the information indicates to general purpose web components (browsers, proxies, caches, spiders....) the semantics (meaning) of the fields and response body (ex: does the message include a representation of a resource or a representation of an error?)
Bespoke HTTP message handlers (and human operators) are expected to look for information in the body (ex: a 404 for a web page returns a picture of a fail whale and a bunch of links to different resources that might clarify what's gone wrong).
You can also leverage ideas like web linking (RFC 8288), if you want to describe relationships between the error and other resources.
Problem Details (RFC 7807) describes a standardized JSON schema for communicating error information, if you want a JSON representation but prefer not to do all of the schema design yourself.
CodePudding user response:
First and foremost, REST has no endpoints but resources.
there should be some way of differentiating between "This endpoint is malformed" and "there is no record in the table."
By "This endpoint is malformed" I guess you probably mean the request issued to the server doesn't conform to the HTTP specification. As voice already mentioned, HTTP status code are coordination metadata for the outcome of the transportation and not necessarily the outcome of your business logic. Of course you need to come up with a mapping for problems you noticed while applying your business logic to the HTTP transport domain.
Unfortunately, REST is polluted with false assumption and believes. Plenty of people seem to think of it as HTTP based CRUD mostly done with JSON payloads. But this is just a very tiny fraction of what REST really is. At its heart it is a technique used in distributed computing to help decouple clients from server to allow the latter to evolve freely in future. Clients on the other hand are build with the inherent design decision of a possible change in mind and therefore get much more robust towards change in the end.
So, how does REST help to decouple clients from servers?
- First, the spelling of a URI is not of importance. The URI needs to be a valid one but that's it basically. Clients shouldn't parse the URI or try to extract some knowledge off the URI nor does a URI pattern like
/api/user/1
and/api/user/1/stuff
mean that both of those URIs are somehow related. That's what link-relations are there for. - Next, in order to teach a client what an URI returned by the server is good for URIs should come with one or multiple link-relation names, which should either be based on registered ones or at least follow the Web Linking extension mechanism, which basically is just a further URI that does not necessarily need to point to a valid resource. Treat it like a predicate in a (SemWeb) ontology.
- Use forms similar to HTML, like HAL-forms, JsonForms or Ion, if your server needs further input from clients. Forms also teach clients on what HTTP methods to use, which URI to send the request to, what media-type to encode the request in and of course a description of the properties the resource has and/or the server expects input for. This information is enough to let a client send valid HTTP requests in terms of the transport domain to the server. Note that this does not mean that there won't be any issues then. Requests still might fail to reach the server due to internet outage on whatever end, the request being routed badly and exceed the maximum number of allowed hops and so on but depending on the HTTP method used for sending the request a client might automatically reissue a request once it hit its timeout threshold.
- In order to increase interoperability of any peer in a REST ecosystem REST has a strong focus on media types. Think of it as the binding contract between a client and a server which should be negotiated between both of them. This guarantees that both are capable of exchanging "messages" both understand and are able to process. One of the difference to regular RPC services here though is that RPC services are usually restricted to one payload mechanism while REST supports more or less an unlimited amount of payloads, depending on its support for various media-types. Media types are a human-readable description on how payload should be encoded and processed and also contains information, besides the syntax description of allowed elements, a semantic description on the purpose of the respective elements. A payload issued for plain
application/json
doesn't teach a client really what the properties of the respective JSON objects used in the payload mean nor does it really support URIs in first place. Note however that issuing a plain JSON request to the server is fine if the client was "instructed" that way using a form the client was acting upon. The server here just expects that kind of payload then. Just look at how a typical HTML document is build up and read up on some of the tag definitions that are used within the HTML document and you might get the gist of this paragraph.
Especially about the latter two points Fielding himself was quite vocal about in his famous rant:
A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types ...
So, back to the actual question at hand. Is "there is no record in the table" really a business logic error? You could also design it to return what's currently available there and return an empty list. This at least spare you the hazzle of mapping that business error onto the transportation domain in that case.
If you want or need to express a business logic failure to the client you should, as voice also recommended before, look into application/problem json
(or its XML alternative application/problem xml
) which define properties such as type of failure, general title, status and details among others. The respective type the response is issued for may define further properties specific to that type that are part of the payload. I.e. you may define an extension type of http://acme.com/problem/validation
and this extension type defines that the payload needs to contain a target-ref
property to identify the element that failed the validation check as well as a property for the actual error message.
In the end some general recommendations in terms of REST are:
- Design the interactions of client and servers first as if you'd interact with a typical human-focused Web page and then translate the interaction steps onto the application domain. REST in the end is nothing more than a generalized approach for how we humans interact on the Web for decades. REST is basically Web surfing for applications rather than humans. As we humans follow an outlined state machine of i.e. Amazon.com to order some books, computers can do the same. Therefore design the whole interaction between client and server as state machine that clients just follow along and may exit at certain points
- Allow servers to teach clients what they need to know using various form-support and use link-relations to set given URIs in context to the current resource