3

Note the following from Roy Fielding concerning REST design, guidelines & principals.

5.2.1.1 Resources and Resource Identifiers

The key abstraction of information in REST is a resource. Any information that can be named can be a resource: a document or image, a temporal service (e.g. "today's weather in Los Angeles"), a collection of other resources, a non-virtual object (e.g. a person), and so on. In other words, any concept that might be the target of an author's hypertext reference must fit within the definition of a resource.

A resource is a conceptual mapping to a set of entities, not the entity that corresponds to the mapping at any particular point in time.

More precisely, a resource R is a temporally varying membership function MR(t), which for time t maps to a set of entities, or values, which are equivalent. The values in the set may be resource representations and/or resource identifiers. A resource can map to the empty set, which allows references to be made to a concept before any realization of that concept exists -- a notion that was foreign to most hypertext systems prior to the Web [61]. Some resources are static in the sense that, when examined at any time after their creation, they always correspond to the same value set. Others have a high degree of variance in their value over time.

The only thing that is required to be static for a resource is the semantics of the mapping, since the semantics is what distinguishes one resource from another.

The key points have been bolded, the rest of the paragraph I have included is for context.

Here is the scenario.

I have a web api that has a endpoint: http://www.myfakeapi.com/people

When a client does a GET request to this endpoint, they receive back a list of people.

Person
{
  "Name": "John Doe",
  "Age": "23",
  "Favorite Color": "Green"
}

Ok, well that's cool.

But is it against REST design practices and principles if I have a 'Person' who does not have a Favorite Color and I want to return them like this:

Person
{
  "Name": "Bob Doe",
  "Age": "23",
}

Or should I return them like this:

Person
{
  "Name": "Bob Doe",
  "Age": "23",
  "Favorite Color": null
}

The issue is that the client requesting the resource has to do extra work to see if the property even exist in the first place. Some 'Person's' have favorite colors and some don't. Is it against REST principals to just omit the json property of 'Favorite Color' if they don't exist - or should that property be given a 'null' or blank value?

What does REST say about this? I am thinking that I should give back a null and not change the representation of the resource the client is requesting by omitting properties.

Yusha
  • 1,496
  • 2
  • 14
  • 30
  • Is a HTML page invalid just because it contains no form element even though the media type states there could be some? In other words, you can only return what you have as information. Whether you include some arbitrary fallback value is your design choice or a detail of the media type you exchange messages for. Note that I flagged this question as primarily opinionated as there isn't a clear answer to that and it basically comes down to personal preferences – Roman Vottner Jan 24 '20 at 11:39
  • Kind of a different analogy, but the real question is - if the client is expecting a JSON object with three properties, but sometimes I decide to send him that same object but with two properties, is this **1) This a violation of REST**, and **2) Bad practice**? I understand how adding properties is fine as this is a non-breaking change, omitting properties seems like it would definitely be a 'breaking change', or maybe I am confused? – Yusha Jan 25 '20 at 13:52
  • First thing to clear up is, REST isn't a protocol, so you can't violate it. REST is just a collection of constraints that when not followed correctly will not provide the benefits REST aims for. Next, client are taught everything they need to know by servers. A form i.e. will teach a client on the expected data elements, the representation the server expects, the method to send the payload with as well as the endpoint to send the data to. At no point a client should need to consult an external documentation. – Roman Vottner Jan 25 '20 at 14:20
  • If a client expects to receive certain fields in a response, it probably considers resources to have a [certain type](http://soabits.blogspot.com/2012/04/restful-resources-are-not-typed.html), which, according to [Fielding](https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven) himself, a client should never do as this just introduces a tight coupling of the client to a specific API and wont allow the same client to interact with a different API easily – Roman Vottner Jan 25 '20 at 14:44

2 Answers2

1

Off the top of my head I can't think of any REST constraints that this violates (here's a link to a brief overview if you're interested). It also doesn't violate idempotency for a GET request. However, it is still bad practice.

The consumer of your API should know what to expect and ideally this should be well documented (I like using Swagger a lot for this). Any changes in what to expect should be communicated to consumers, possibly in the form of release notes. Changes that could potentially be breaking for your consumer should be delivered in a new version of your API.

Since your Person1 and Person2 are technically different object structures, that could be breaking in itself (let's face it, we don't always find the edge cases as devs). You don't just want your API to work on a basic level and to hell with the end users - you want to design it with the end-consumer in mind so that their lives are made easier.

Ashley
  • 897
  • 1
  • 5
  • 17
  • Yes, I said they are different object structures. If that quote is what is concerning you then maybe this comment from another user can clarify that for you: https://stackoverflow.com/a/54903700/1168004 – Ashley Jan 23 '20 at 23:49
  • As you can see the intent is not about a particular object structure and the mapping of the details of the object, but the mapping to the specific resource. Aka which person we are getting, not what the structure of the person is. The paragraph the user above quotes is different than the one in the version I read but I think it's clearer. – Ashley Jan 23 '20 at 23:54
  • To your first comment - sorry, I misread what you were saying. I definitely agree with you that they are different object structures. To your second comment, the documentation clearly states **The only thing that is required to be static for a resource is the semantics of the mapping, since the semantics is what distinguishes one resource from another.** I am considering the 'semantics' as the object properties; Name, Favorite Color etc. I can have two shoes, but they obviously aren't the same shoe if one is a size 13 and one is a size 10. – Yusha Jan 24 '20 at 01:58
  • I see what you are saying and your confusion, but the semantics in question here are those of the mapping to the resource and not to the details of the resource itself. The semantics of the *object* may be its properties, but those of the *mapping* are how a resource is identified: "For example, the "authors' preferred version" of an academic paper is a mapping whose value changes over time, whereas a mapping to "the paper published in the proceedings of conference X" is static." – Ashley Jan 24 '20 at 13:07
  • Wow, thanks for the clarification. I still can't believe that this isn't a violation of REST. I think now though I can safely declare it as 'bad practice' as you mentioned above earlier. Thanks for listening! – Yusha Jan 25 '20 at 13:45
  • Swagger and similar documentation tools are only needed for RPC APIs! A Web browser does not need to look up any particular API documentation in order to interact with a Web page. REST builds upon the concept of "server teaches clients what they need to know" by i.e. utilizing HTML forms to teach a client what input data the server expects, in which representation format to send the data with and the actual target location as well as the HTTP method to use. At no point in the application domain protocol does a client need to consult an external API documentation :) – Roman Vottner Feb 18 '20 at 16:14
  • To your last paragraph, your primary goal should be to remove any coupling that prevents your from evolving your service in future. This is exactly what REST aims at. In order to achieve decoupling a huge amount of discipline is necessary to avoid introducing coupling. Most business takers or developers furthermore act to short-sighted and don't see the necessity for evolvable software in general, hence they prefer quick and dirty solution which gets "fixed" for the worse in future until a point is reached that requires a complete redesign/rewrite of the whole system as it turned unmanagable. – Roman Vottner Feb 18 '20 at 16:28
1

There are various ways we can deal with this, depends upon the use case, I'll list them only by one

1) Prefer enums (only if it makes sense to your use case)

{
  "Name": "Bob Doe",
  "Age": "23",
  "Favorite Color": NO_COLOR
}

When you know the values for your property at the beginning, define a set of enum constants, and assign a default value if the property does not apply to the user. This helps in a few ways:

  • Your client knows what are the possible values so they can prepare their client system accordingly.

  • By giving default enum constant, we convey that value of the particular field is successfully retrieved from either persistent storage or maybe from another remote service, but it has default value because the property may not apply to the user OR user doesn't have any value for this property.

  • By avoiding NULL pattern, your client code will be resilient and the client can prepare their code for default enum constant.

  • When you start to serve more users, you may need to add a few more enum constants which may not apply to every client of yours. When you add new enums which they don't know, they can easily handle this in their parsing libraries and convert into something as per client application design. In Jackson, we can use DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL for this.

2) Use Null - Do not create enum constants for everything

There are cases perfectly valid to have a NULL object. For instance, in the below example, it makes sense to use null if there is no favourite quote.

{
  "Name": "Bob Doe",
  "Age": "23",
  "Favorite Quote": null
}

3) Document your required properties clearly

If you use swagger for your rest API documentation, you can mark mandatory properties as required. The ones not marked are optional. In that way, the client will be prepared to handle if they are NULL or empty string. (It should apply to other API documentation tools as well)

Bad practice: I notice a few users code in such a way, they send errors in the same response model they send their success response 200. Refer this question & answer. This is definitely a bad practice. Don't mix two different responses and mark one property as optional - use status codes to convey any problems. I'm not talking about partial response here.

4) Add/Modify properties (as long as you're not breaking a contract with the client)

Say the Favorite Color property is added later and currently you're sending the following response to your client. You will publish your new contract to your clients when you add Favorite Color, but your clients should have fail-safe code and they should handle the unknown properties. In Jackson, we will use DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES for this. Non-breaking changes do not necessarily require v2.

Person
{
  "Name": "Bob Doe",
  "Age": "23",
}

So, answer to your question is, you should start looking at the first three options while you design your rest API, you don't require to omit any properties. But, you may be required to add a few properties later(covered at #4), which is perfectly fine.

prakashb
  • 160
  • 1
  • 8
  • To be clear though, **omitting properties based on the 'Person' being returned is not a 'violation of REST', but just 'bad practice'** correct? – Yusha Jan 25 '20 at 13:49
  • I even won't call it a bad practice because if the situation demands you may need to do this. Look at this [answer](https://stackoverflow.com/a/27090954/1480381) which talks about API versioning. If you want to maintain two versions of your API, yes you'd need to do something similar to the answer. I'd say, if your situation requires, you can do this for compatibility. But when there is no such situation and still you design your application in this way, then this can be a *bad practice.* – prakashb Jan 25 '20 at 15:04