Rest – Consequences of not doing a REST API the “right” way

apidesignreststandards

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?

Best Answer

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.
Related Topic