To start with, there is no such thing as a "RESTful standard" or a "RESTful URL/URI/IRI". Roy Fielding defined an architectural style, which he termed "REpresentational State Transfer" or REST in short. According to Robert C. "Uncle Bob" Martin an architecture is about intent and the intention behind a REST architecture is the strong decoupling of clients from server implementations to allow the latter to evolve freely in future, similar to browsers and servers where a browser does not need to get updated when a server suddenly changes its URI structure or page layout. Thereby REST introduces a couple of constraints that when followed consistently will reduce the likelihood of coupling and allow to benefit from the promises REST attempts to provide.
In most cases the communication in a REST architecture is designed around HTTP, though REST is not limited to HTTP in particular. HTTP itself is also just a distributed document management system whose application domain is the transfer of documents over the Web. So, in essence, HTTP is just responsible for transferring one document from one machine to an other. The management of the documents may cause side-effects that lead to certain processings of that documents which may further trigger some business rules run at the servers.
The whole interaction between clients and servers is based on the premise that a server should teach its clients everything they need to know in order to make informed choices on which actions to perform next. On the Web we are just so used to receiving links and forms in our browsers that this already feels natural. The HTML media-type defines the semantics of i.e. forms or links so that a browser that finds such a markup is able to render a page with that information and present it to the user. Based on the affordance of certain elements a client (user) is now guided through the process of retrieving further, adding new or modifying or removing existing data. The form element here teaches a client a couple of things at the same time. A client will not only learn which kind of data the server expects but also the URI where to send the data to, the representation format the server would like to receive as well as the HTTP operation to use on sending the payload to the server. Here, no external documentation is really needed, except for possible user hints on what certain input fields may express (CCV field on a credit card payment page, or some technical abbreviations, ...).
Here the media-type client and server act upon is a key-part in the whole handling. A media-type, such as application/html
, defines the syntax and semantics used in a document and allow an application to process such a document further. It is important to know that a resource does not have a particular type per se but is representation format should be negotiated between client and server. I.e. if you take a car manufactures details page of a particular car model it would be easy to assume that an URI such as http://some-vendor/cars/2019/some-brand/model
may return details on that particular car model. Though the URI itself is just a pointer to a resource that may return such data, or some completely other date, such as an image of a women posing in front of some car or something totally different. The important part here is, one should not rely that a URI does state anything about its content.
Exchanging data via application/json
though is not that suitable in a REST architecture. While plain JSON defines the general syntax to use, it lacks support of expressing the semantics of available fields. While JSON Hyper-Schema (application/schema+json hyper-schema
) or JSON Hypertext Application-Language (HAL) (application/hal+json
) add support for links and link-relations, they still cannot express what kind of data you are really processing. Through the usage of certain media-type profiles semantic refinement can be added to a generic media-type. I.e. application/hal+json;profile=http://schema.org/Car
could be used to state that the data follows the semantics of a car definition as defined by schema.org. A processor of that document can then lookup the supported fields schema.org has for this particular type and validate accordingly.
Relying on content-type negotiation will prevent a direct coupling between clients and servers but instead introduce a direct coupling of each stakeholder to the respective media-type format. As both parties however agree upon a representation format to exchange a payload for, both should be able to process the data and thus reduce interoperability issues.
In plenty of questions here at SO people are looking for ways to improve their design from a performance aspect and plenty of users recommend to reduce the number of interactions to a bare minimum by packing everything into one single request at best. However, HTTP is less than ideal on performing batch-operations. I somehow disagree, therefore, with such a recommendation per se as it bypasses a very important aspect of the whole interaction chain that is in heavy use on the Web: Caching. Caching is really efficient when the same data is request over and over again and the data does not change frequently. I.e. the details of a car might not change that often, thereby neglecting caching and retrieving the data all over again may be a waste of server resources if the same data was retrieved just a minute ago. A cache will automatically invalidate any stored representation for a given cache-key (=URI including query parameters) if a non-safe operation is performed on the requested resource or if the freshness value of that URI in the cache is older than a certain threshold defined in the Cache-Control
header. As such, a rule of thumb should be to make use of caching as much as possible.
In regards to your actual question: a rule of thumb here should always be that the backend will check the inputs first before performing its task. In case of your user example, it should check whether the user exists before creating a new one. Usually a user is stored in a DB and they imply a unique key constraint on that entry which kicks in when you attempt to add the user a second time. As such, even when you first check if a user exists via a GET
operation first and the server returns you a 404 Not Found
this is no guarantee that at the time the POST
request hits the server an other client hasn't created that user in the meantime. As such, in 1) you might do unnecessary work. My issue in 2) is though that it seems like your frontend already knows what your backend expects. While this is natural for a frontend-to-backend system, this somehow contradicts REST principles as the frontend assumes that the resource endpoint serves a certain type and can directly send that data to the backend server. No content-type negotiation, no teaching what the server expects, ... Therefore, in terms of "RESTfulness" my suggestion would be to let the frontend ask the backend to teach it how a user needs to be created. The backend might respond with a media-type representation that supports forms, such as hal-forms or halform and then map that representation to dynamically create a HTML form in your Angular or ReactJS frontend (or whatever application your frontend is running in) that is rendered to the user. This way you decouple the frontend from having to know what the backend expects and is able to present new or updated fields to the user automatically without requiring a change in the frontend directly. If multiple users should be created within a reasonable short time, the form representation may be reused from the cache and thus avoid further requests sent to the server. Of course the actual creation request needs to be sent to the server.
I admit that applying all these to a simple fronted-to-backend system is overkill, but such a scenario is for sure not the case REST was designed for initially.