8

I'll ask this question this way - what are the software engineering concerns for not implementing my REST API the "right" way?

What do you mean the "right" way? Well, allow me to explain my perception of the right way, then I'll tell you how I am doing it (also, assume I am talking about a JSON REST API).

The right way

  1. Statelessness. This is the part I do get. The client maintains the state always 100% the time forevermore. It's not the server's job, it's the client's.

  2. The expected actions and response for each verb:

    • GET - Gets a resource specified in full entirety, only limited by either the authorization in the request or a query parameter. This assures no modification of any resource in the process.
    • POST - Given an entire resource description (like a JSON object), creates a resource, then returns that resource, with any server side properties also created, such as dates or IDs.
    • DELETE - Deletes a specified resource, giving only some sort of 200 OK as the response
    • PUT - Given an entire object declaration as input, updates the resource at a specific location, updating all fields of the resource to each of the fields given in the input. To be clear, this expects the entire object to be passed in as input. The entire updated resource is returned, with all fields (according to the authorization or any other input flags).
    • PATCH - Given only the fields wished to be modified for a resource, updates just the fields in a specified resource that are given as input. (This is where I am unclear): The entire resource is returned? (Or is it just the updated fields? Dunno. Don't care.)
  3. Resource paths. Given the relationship of the resources to each other, a resource path may look like one of:
    • /parentresource/:id
    • /parentresource/:id/childresource
    • /parentresource/:id/childresource/:childId
    • /parentresource/:id/childresource/:childId/subresource/:subresourceId (In this example, a subresource belongs to a childresource, which belongs to a parent resource).

The way I want to do it

The above is my understanding of how a REST API is supposed to work. Now let me list some of my variations to the above:

  1. PUT/PATCH - What is the point of passing in the entire resource for modification? I only use PUTs to modify resources, and I only pass in the fields I want to be updated. As a result, I have no need to use PATCH
  2. Resource Paths - I use GUIDs in my application. As a result, they will be globally unique. Why do I need the full resource path, including the parent resources, if I can just uniquely refer to a subresource by itself? Like:
     
    /subresource/:subresourceId
     
    If I were to do it the "right" way, trying to reference the subresource would require a full path like:
     
    /parentresource/:id/childresource/:childId/subresource/:subresourceId
     
    Is all that necessary? Because now I have to have additional error handling if my path contains a :subresourceId that is not actually owned by a given :childId, and ditto for a :childId not owned by a parent :id. My server side is taking care of resource authorization. Can't I just reference the resource itself, rather than the full path?

  3. The return response. Let's say, for example, that my data structure is a hierarchical tree, with no practical limits on tree depth. Resources lie at different levels down the tree, in a hierarchical fashion.

    • The GET is obvious. If I get this entire tree, I expect the entire tree as a response, with resources contained within resources.
    • If I POST to create a new resource, PUT to update, or DELETE to remove, I want to see the deltas in the tree, rather than just seeing the resource that I created/updated/deleted. I don't want to have to again call the GET of the parent tree after every POST, PUT, or DELETE, especially if there are little changes to the tree and I only want to see the deltas.

Hopefully my questions are clear.

If you were to see a REST implementation as I described it, would you gawk at it and tell me of your software engineering concerns? If so, what would they be?

  • 4
    Curiously, none of your concerns involves the architectural constraints of REST. Your questions are strictly bound to the WEB and HTTP semantics. Looks like you are not comfortable with these last, what makes me think, Why to make a web interface at all? On the other hand, there's no such thing like "right way" or "wrong way". Just implementations that best suite your needs. What you call "right" it's just more "web-friendly" and "developer-friendly". The more "web-friendly" is, the more advantage it takes from the WWW architecture. That would be the point of doing web "things". – Laiv Mar 8 at 7:39
  • The risk of deviating from REST is that someone expecting a RESTful interface will be confused by the behavior of your web application. However, what you suggest doesn't even seem to deviate that much to cause total confusion, so I would argue that what you're suggesting is perfectly fine. Just add documentation! – Neil Mar 8 at 8:05
  • @Laiv, you pose a good question - why make a web interface at all? Because I am not familiar with any other architecture that supports a client-side rendering framework such as React or Vue, than to have a web interface that sends the data that I need. To be clear, I do not want server-side rendering, for flexibility and performance reasons. I also know that GraphQL is an option, but I am terribly unfamiliar with it. – Michael Plautz Mar 11 at 15:28
  • GraphQL is not an architectural style is just one more query language with a lot of boiler-plate code under the hood. you can take advantage of the WWW without going REST. There's yet WebSockets or ProtocolBuffers. if your application is not purely representational stat transfer, then REST doesn't serves you. – Laiv Mar 11 at 16:04
3

The general underlying answer here is that your ideas may work on a technical level, but that doesn't mean they conform to the standardized conventions of REST.


  1. PUT/PATCH - What is the point of passing in the entire resource for modification? I only use PUTs to modify resources, and I only pass in the fields I want to be updated. As a result, I have no need to use PATCH

Your idea works on a technical level, but it's simply not how REST has been described. Keep in mind that any discussion about working code (i.e. no compile or runtime errors) is always going to be a matter of convention, not necessarily of clear technical superiority.


  1. Resource Paths - I use GUIDs in my application. As a result, they will be globally unique. Why do I need the full resource path, including the parent resources, if I can just uniquely refer to a subresource by itself?

There are many nuances to how we define "child/parent" entities. Most commonly, it refers for a one-to-many (parent-to-children) relationship.

However I suspect that for REST, part of what makes a child a child is that there is an expectation of only being able access them through the parent, that they don't carry their own globally unique (and externally known) identifier.
I suspect this is following the same philosophy (but not necessarily for the same reason) as that of aggregates (and their roots) in Domain Driven Development.

A DDD aggregate is a cluster of domain objects that can be treated as a single unit. An aggregate will have one of its component objects be the aggregate root. Any references from outside the aggregate should only go to the aggregate root.

In your case, what you call the "parent" functions as the aggregate root. The single point of contact (if you will) for outside callers.

You might want to conclude from this that your child is actually a different aggregate. That may be the case, but I do want to issue a warning with that decision. You shouldn't base your architecture on the particular type of a field. You have no way of knowing if you will always keep using globally unique IDs for all your entities. If that ever changes, for whatever reason, you're going to compromise the viability of your REST architecture; as you may end up in a situation where the child is no longer uniquely identifiable and thus needs to be referenced through its parent.


  1. If I POST to create a new resource, PUT to update, or DELETE to remove, I want to see the deltas in the tree, rather than just seeing the resource that I created/updated/deleted.

You're violating the order of operations of the design. A REST API is specifically intended to be consumer agnostic. The API should not be built according to the specifications of one of its consumers.

When you say "I want to see the deltas in the tree", what you're really saying is "the consuming application only has a need to see the deltas in the tree". But that doesn't quite matter to the REST api. It merely provides a standardized approach.

It is the nature of standardized approaches to often lack highly customizable tools, and instead favor the most commonly used tools.


Can you deviate from the path? Well, it will work on a technical level. But it won't be pure REST anymore. This is something that is highly contextual and you need to weigh the options.

  • If you're creating an API that is expected to cater to many varied consumers, then I suggest sticking to REST as best as you can.
  • If you're building an API that will only have one consumer which is also developed by you; then there's no real need to stick to pure REST.
  • Straying from the path means you're going to have to document how you've strayed so other developers can still make sense of it. If you stick to pure REST, you don't have to write the documentation and the other developers don't have to spend time and effort figuring out your customized approach.
9

what are the software engineering concerns for not implementing my REST API the "right" way?

The primary one, to my mind, is the ability to delegate out work to generic components that know only the standards, not your specific business case.

If you are adhering to the uniform interface, then it is easier for other parties to build components that integrate nicely with yours.

Here is Fielding writing in 2008

REST is intended for long-lived network-based applications that span multiple organizations.

One of the ways we managed "long-lived" is by having a clear standard describing the semantics of the messages we are passing. If everybody agrees what PUT means, then consumers and producers of those requests can be developed independently, and intermediary components between the two can take sensible actions without needing to know the details of the message in your specific context.

PUT/PATCH - What is the point of passing in the entire resource for modification? I only use PUTs to modify resources, and I only pass in the fields I want to be updated. As a result, I have no need to use PATCH

What's the point in using PUT then?

PURPLE /014d8c83-604d-4cf0-a6ba-e1f7ef8c4898 HTTP/1.1
...

That's a perfectly valid request line for an HTTP message, and the change in semantics won't confuse anybody.

Equivalently

POST /014d8c83-604d-4cf0-a6ba-e1f7ef8c4898 HTTP/1.1
...

Which pretty much has unconstrained semantics; the server can implement the handling of that request any way it wants.

assume I am talking about a JSON REST API

Your reluctance to use PATCH is especially odd in this case, as there are already proposed standards for JSON Patch and JSON Merge Patch -- the work of standardizing a patch document format may already be done for you.

Another valid alternative would be to treat the patch document as a separate resource. Semantically, you might imagine something like

PUT /014d8c83-604d-4cf0-a6ba-e1f7ef8c4898/patches/5c42c414-03c0-4ac5-af14-2b1165ac98b3 HTTP/1.1

That gets you honest, uniform, message semantics, sacrificing standardized cache invalidation.

In a code review setting, I would reject a proposed change that attempted to redefine the semantics of PUT.

HTTP does not attempt to require the results of a GET to be safe. What it does is require that the semantics of the operation be safe, and therefore it is a fault of the implementation, not the interface or the user of that interface, if anything happens as a result that causes loss of property (money, BTW, is considered property for the sake of this definition). -- Fielding, 2002.

The same consideration holds for PUT as well; if your implementation of PUT deviates from the standardized semantics, then your implementation is responsible for the resulting damage.

Resource Paths - I use GUIDs in my application. As a result, they will be globally unique. Why do I need the full resource path, including the parent resources, if I can just uniquely refer to a subresource by itself?

That's perfectly fine. REST doesn't care what spellings you use for your resource identifiers.

Consider the google landing page. Do you need to pay any attention to the spelling of the URI for today's doodle? or where the search form is being submitted? No, of course, not. The HTML payload includes URI in it, and the clients just use the identifiers that are provided, in standard ways, without needing to analyze those identifiers.

Information encoded into a URI is at the discretion of the origin server, for its own purposes.

I would discourage using such a URI as the entry point of your API. https://www.example.org/df8f5f87-15ff-4212-8fb8-4fbca2c7efcf is a little bit awkward for human consumption. A human readable URI that redirects to the UUID resource would be fine, a human readable URI that returns the content of the UUID resource would be better.

If I POST to create a new resource, PUT to update, or DELETE to remove, I want to see the deltas in the tree, rather than just seeing the resource that I created/updated/deleted

That's fine -- again, look at the standard.

The payload sent in a 200 response depends on the request method. For the methods defined by this specification, the intended meaning of the payload can be summarized as

POST a representation of the status of, or results obtained from, the action

PUT, DELETE a representation of the status of the action

In some cases, it makes sense to send the new representation of the resource as part of the response (to spare the client the latency of a GET request/response),.

  • 2
    What's the point in using PUT then? -- Indeed. A POST would work perfectly fine. – Robert Harvey Mar 9 at 16:05
  • @RobertHarvey Or why not use PATCH? The definition fits better. – Solomon Ucko Mar 13 at 21:00
  • @SolomonUcko: Making everything a POST has the virtue of simplicity. – Robert Harvey Mar 13 at 21:34
3

REST is an architectural style for manipulating resources in a stateless manner (where stateless means that each manipulation stands on itself and does not depend on other manipulations that might have taken place).

The use of the verbs PUT/PATCH/POST/GET/DELETE comes from the common use of the HTTP protocol used to transfer and manipulate the resources. The meaning of those verbs is defined by an internet standard (RFC7231).

Given that background, your use of PUT is non-standard and may confuse other developers that want to use your API.

Regarding the resource paths, hardly anyone cares about their exact spelling (including if a child resource is listed as being a child). What people care about is that each resource is uniquely identified. The system /parent/:pid/child/:cid is often used when the child id's are only unique within one parent to still have a globally unique path to the resource.

  • 2
    In addition to Bart's answer. Just recall that the only who must read and understand the URI is the HTTP Client, and it doesn't care about the hierarchy of the resources. So whether the URI express hierarchy, relationship or any other kind of relationship is only meaningful for the poor living being who needs to read it – Laiv Mar 8 at 8:07
1
  1. PUT/PATCH - What is the point of passing in the entire resource for modification? I only use PUTs to modify resources, and I only pass in the fields I want to be updated. As a result, I have no need to use PATCH

Seems to me that you really should be using PATCH as the semantic here.

  • PUT explains the exact desired state. This is useful when a resource might be changing frequently, and the desired change needs to occur within a specific context.

  • PATCH explains a desired delta to whatever is there. This is useful when the change doesn't care about the context, or the relevant context is a lot smaller than the entire resource.

An image is a good example were it would make sense to just upload the whole thing. It is important that the whole resource is communicated to ensure a relevant context.

Conversely updating the play count on a music playlist might make better sense to be a delta. Not because its a small change, but because resubmitting the entire list could easily undo changes to the content of the list.

  1. Resource Paths - I use GUIDs in my application. As a result, they will be globally unique. Why do I need the full resource path, including the parent resources, if I can just uniquely refer to a subresource by itself?

...snip...

Is all that necessary? Because now I have to have additional error handling if my path contains a :subresourceId that is not actually owned by a given :childId, and ditto for a :childId not owned by a parent :id. My server side is taking care of resource authorization. Can't I just reference the resource itself, rather than the full path?

Nope you really don't need to use paths - ever. Say do you keep all of your files on the desktop? No? Why not?

Probably something to do with making it easier to see, same problem here. A GUID doesn't tell you what you are handling while you are setting this up, debugging it, or running it.

So what does it feel like to support this system? or to interact with it? If you haven't thought about it take some time and consider the work you are pushing down the line.

Having explicit pathing information serves to help validate the request. It also helps to differentiate information enough that support and downstream system developers can approach these urls and use them.

  1. The return response. Let's say, for example, that my data structure is a hierarchical tree, with no practical limits on tree depth. Resources lie at different levels down the tree, in a hierarchical fashion.

    a. The GET is obvious. If I get this entire tree, I expect the entire tree as a response, with resources contained within resources.

You may want to impose a depth limit, simply so that some smart kid doesn't simply get the root of your site for every operation they can think of.

b. If I POST to create a new resource, PUT to update, or DELETE to remove, I want to see the deltas in the tree, rather than just seeing the resource that I created/updated/deleted. I don't want to have to again call the GET of the parent tree after every POST, PUT, or DELETE, especially if there are little changes to the tree and I only want to see the deltas.

If updating a resource is affecting its parent in a non-trivial and predictable manner you have other issues... You really need to look at the state model and figure out why the information is jumping around.

If you only want to return an itemised list of deltas, why not? Why not support several output views toggled by various parameters? Jenkins returns its API responses in a choice of xml, or json and allows you to specify several filters for extracting the desired sub-tree.

Use It Yourself

Frankly though, step back from what you are making and try to support it, or make another application to use it (not one of your pre-existing applications). Do something similar for third-party API's so that you have a little background context.

Whenever you find yourself having to do something that isn't directly solving the support request, or directly necessary to the client application then the API is less than ideal, and you know why it is less than ideal which is even better, because you can fix it or not make the same mistake.

For example if requests to a given url are constantly failing, how much effort do you have to invest to determine what is failing and why? What steps did you take, could you have avoided one of those steps by having a better URI, or better logging, or better monitoring, or etc...?

Similarly, if you are writing a new client, how many times do you need to refer to the documentation, or to the API's source code? What could you do to reduce that need? What could you do to stop violating your own expectations? What could you do that simplifies the client applications problem without making the server a nightmare to maintain?

Right way

Frankly the right way is circumstantial. REST is a set of practices shown to work across a number of circumstances for a number of problems. If your problem doesn't fit please don't make it fit, but then also don't claim to use those practices.

-1

Most of the features of REST apis are there for a reason, but it might not be a reason that is relevant to you. Giving the entire resource, as in PUT, for example is relevant if you need idempotency, otherwise it isn't. (Though I think it would be nicer towards users/colleagues/whatever to advertise the fact that your endpoint is not idempotent by using POST or PATCH instead.)

For the path thing I have never heard that being related to rest. /root/345dd4dc-e175-455f-b545-85b1b1ce3e82 is as much part of a tree as /foo/bar/baz. Maybe a little less user friendly but not less resty as far as I can see.

If you want more detailed reasoning about why REST was designed as it is I think you should go read the original dissertation: https://www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf

Reading that you might discover that it's quite different from how REST is represented in conversations or used in APIs today. Clearly a lot of other people have found reason, good or bad, to depart from it.

I particularly like this quote which you might find relevant:

HTTP is not designed to be a transport protocol. It is a transfer protocol in which the messages reflect the semantics of the Web architecture by performing actions on resources through the transfer and manipulation of representations of those resources. It is possible to achieve a wide range of functionality using this very simple interface, but following the interface is required in order for HTTP semantics to remain visible to intermediaries. That is why HTTP goes through firewalls. Most of the recently proposed extensions to HTTP, aside from WebDAV [60], have merely used HTTP as a way to move other application protocols through a firewall, which is a fundamentally misguided idea. Not only does it defeat the purpose of having a firewall, but it won’t work for the long term because firewall vendors will simply have to perform additional protocol filtering. It therefore makes no sense to do those extensions on top of HTTP, since the only thing HTTP accomplishes in that situation is to add overhead from a legacy syntax. A true application of HTTP maps the protocol user’s actions to something that can be expressed using HTTP semantics, thus creating a network-based API to services which can be understood by agents and intermediaries without any knowledge of the application.

  • 2
    "Though I think it would be nicer towards users/colleagues/whatever to advertise the fact that your endpoint is not idempotent by using POST or PATCH instead." – This is not a question of nicety. The HTTP methods have precisely defined semantics, and the entire world-wide web infrastructure depends on those semantics. For example, proxies may retry PUT requests multiple times without you knowing, and they are allowed to do that because the HTTP spec says that PUT is idempotent. If your PUTs are not idempotent, then this will break your service. – Jörg W Mittag Mar 8 at 9:50
  • 2
    For a time, "web accelerators" were all the rage, and in fact, they are making a comeback for mobile devices. Those accelerators did pre-fetching and cacheing of resources, and they were allowed to do that, because the HTTP spec guarantees that GET and HEAD are pure and side-effect free. Some people lost data, because they were using badly-designed web apps, where deletion of content was done with a GET request, and the web accelerator happily sent GET requests to all URIs it could find. – Jörg W Mittag Mar 8 at 9:53
  • You are right of course. I just can't work up any enthusiasm regarding a properly working web anymore. It feels like a lost battle to me, but maybe you are in a nicer environment where things like these actually work. Where I am http is just a transport with the addition that it's debugable from a browser instead of netcat, and that is all. – monocell Mar 8 at 10:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.