-
Notifications
You must be signed in to change notification settings - Fork 890
Standardize and clarify success (2xx) responses #1638
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Uses consistent language across requests that create, update, and delete resources and relationships. Re-orders success codes to be in ascending order for consistency. Clarifies that it is always ok to return a 200 (or 201 in the case of creation) response with a document that does not include primary data when the server does not change the targeted resource(s) / relationship(s) in any way different from what was requested. This allows the server to include other top-level members, such as `meta`, in responses without primary data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for addressing this one.
Wondering if you want to address #1524 in this one as well or keep it for a follow-up PR. In that case please ignore the two comments related to it.
Beside that one the comments are only about minor points.
_format/1.1/index.md
Outdated
A server **MAY** return a `201 Created` response with a document that contains | ||
no primary data if the requested resource has been created successfully and the | ||
server does not change the resource in any way (for example, if the resource | ||
includes a [Client-Generated ID](#crud-creating-client-ids)). Other top-level | ||
members, such as [meta], could be included in the response document. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a client-generated ID in the request is a requirement for this case, isn't it? Naming it as an example may be confusing. Wondering if calling this requirement out in a separate note would be better from teaching perspective.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a client-generated ID in the request is a requirement for this case, isn't it? Naming it as an example may be confusing.
The whole decision about whether a server must include primary data in all of these sections comes down to whether the server changes the resource(s) beyond what's been requested. The spec can not fully control what that means once we factor in implementation semantics such as meta
data and user-defined fields, not to mention extensions.
Wondering if calling this requirement out in a separate note would be better from teaching perspective.
Let me experiment with this option to keep the normative text tighter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The whole decision about whether a server must include primary data in all of these sections comes down to whether the server changes the resource(s) beyond what's been requested. The spec can not fully control what that means once we factor in implementation semantics such as
meta
data and user-defined fields, not to mention extensions.
Fully agree. But the specification enforces changing the resource object by adding a server-generated ID to it unless request included a client-generated ID. That makes the example problematic in my opinion because in that case it's mandatory not optional. Maybe it could use createdAt
as an example to avoid that confusion and explicitly call out the client-generated ID in a note? Because it's such a very likely case?
_format/1.1/index.md
Outdated
If the requested resource has been created successfully and the server does not | ||
change the resource in any way (for example, if the resource includes a | ||
[Client-Generated ID](#crud-creating-client-ids)), the server **MUST** return | ||
either a `201 Created` status code and response document (as described above) or | ||
a `204 No Content` status code with no response document. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, I would move the client-generated ID out to separate note and naming it as a technical requirement for clarity.
_format/1.1/index.md
Outdated
includes a [Client-Generated ID](#crud-creating-client-ids)). Other top-level | ||
members, such as [meta], could be included in the response document. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other top-level members, such as [meta], could be included in the response document.
This is a little bit confusing as meta
must be included in this case due to this requirement:
A document MUST contain at least one of the following top-level members:
data
: the document's "primary data"errors
: an array of error objectsmeta
: a [meta object][meta] that contains non-standard
meta-information.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been adjusted to allow for extensions that may allow other top-level members.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fear it did not landed or got lost:
Lines 78 to 83 in 25c6982
A document **MUST** contain at least one of the following top-level members: | |
* `data`: the document's "primary data" | |
* `errors`: an array of [error objects](#errors) | |
* `meta`: a [meta object][meta] that contains non-standard | |
meta-information. |
Lines 293 to 298 in 25c6982
A document **MUST** contain at least one of the following top-level members: | |
* `data`: the document's "primary data" | |
* `errors`: an array of [error objects](#errors) | |
* `meta`: a [meta object][meta] that contains non-standard | |
meta-information. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will create a new issue / PR to address this prior to v1.1. This wording should probably be updated to have the same meaning within the base spec, but also clearly allow for extensions to expand possible top-level members.
_format/1.1/index.md
Outdated
@@ -1970,6 +1969,19 @@ server, and we are defining its semantics for JSON:API. | |||
|
|||
#### <a href="#crud-updating-relationship-responses" id="crud-updating-relationship-responses" class="headerlink"></a> Responses | |||
|
|||
##### <a href="#crud-updating-relationship-responses-200" id="crud-updating-relationship-responses-200" class="headerlink"></a> 200 OK | |||
|
|||
If a server accepts an update but also changes the targeted relationship(s) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Relationships in plural is confusing to me. A request to update a relationship using a relationship link can only target one relationship. It may affect more relationships, if changing one relationship has a side-effect on another. But that wouldn't be a "targeted relationship".
Maybe this is not talking about a relationship but instead of the related resources identified by their resource identifier objects?
I raised that point recently in #1524 (comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Relationships in plural is confusing to me.
I used relationship(s)
here because a single request can impact multiple one-to-many relationships between unique resources. Which I think aligns with what you're getting at here:
Maybe this is not talking about a relationship but instead of the related resources identified by their resource identifier objects?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. But I think "relationships" and "relationship" is defined in another way in the specification:
The value of the
relationships
key MUST be an object (a "relationships object"). Each member of a relationships object ("relationships") represents a "relationship" from the resource object in which it has been defined to other resource objects.
In my reading this defines the term "relationship" as a reference from one resource to one or many other resources. That's also the common definition of "relationship" in IT, which I'm aware of. The term "to-many relationship" only makes sense, when a relationship could be a collection of related resources.
What you mean name relationship here, seems to be called "related resources" at other places of the specification. For example in the term "related resource link".
A "related resource link" provides access to resource objects linked in a relationship.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After review, I'm changing from relationship(s)
to relationship
, since a request can in fact only affect one relationship but potentially multiple relationship members. Thanks for calling this out, @jelhan.
_format/1.1/index.md
Outdated
in other ways than those specified by the request, it **MUST** return a `200 | ||
OK` response and a document that includes the updated relationship(s) in its | ||
primary data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does "include the updated relationship(s)" mean? How does an example response payload look like?
Assuming we have a relationship tags
of an articles
resource. Let's assume that currently two resources are referenced by that relationship: { "tags": "1", "tags": 2 }
A client sends a request to add { "tags": "3" }
additionally. Due to some domain-specific constraints the same article can not be related to { "tags": "3" }
and { "tags": "1" }
. The server removes { "tags": "1" }
from the relationship automatically.
What does a response payload look like in this case?
The wording above (and in the specification right now) could be interpreted in two ways:
- Response with all resources currently associated to that relationship.
- Response with the resources, which are added or removed from that relationship due to the update.
The first one would be easy. The server would response with:
{
"data": [
{ "tags": "2" },
{ "tags": "3" }
]
}
The second one is not supported by the base spec. As far as I know the server can not inform the client that { "tags": "1" }
has been removed from the relationship.
Not sure if you want to address this one in this pull request as well. Or keep it for a follow-up PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As the maintainer of JsonApiDotNetCore, the leading server-side JSON:API implementation in ASP.NET, I can say that we're never going to implement option 1. Because it's potentially very expensive to determine and useless to clients, except for theoretical cases. Consider an audit log, where old entries are purged as a side-effect. So POSTing to this to-many relationship would require returning thousands of entries in the response (there's no pagination). Option 2 is so extremely unlikely to ever occur that we can't justify doing extra queries to detect this.
I can see the elegance in using similar statements throughout the spec, but it's pretty impractical when it comes to relationships. Another case is using rowversion/xmin/timestamp columns for optimistic concurrency. They change all the time, but depending on at which side the foreign key is defined, we cannot consistently return that information, so honestly I don't see the point.
From a spec perspective, I think the only sensible solution is to change MUST to MAY in:
If a server accepts an update but also changes the targeted relationship in other ways than those specified by the request, it MUST return a 200 OK response and a document that includes the updated relationship data as its primary data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it's (...) useless to clients, except for theoretical cases.
I disagree with this. It eases handling of client-side caches as the client can be sure that local cache is still up-to-date. Otherwise a client either needs to refetch the data or hope that no side-effects happened to manage client-side cache.
Another case is using rowversion/xmin/timestamp columns for optimistic concurrency. They change all the time, but depending on at which side the foreign key is defined, we cannot consistently return that information, so honestly I don't see the point.
The specification is only talking about changes to the relationship in the sense of additional resources being added to or removed from that relationship than those explicitly requested by the client. Additional meta data a server may store with a relationship (or even attributes of a related resource) are not relevant to determine if "the targeted relationship (changes) in other ways than those specified by the request". At least in my reading of the specification.
I see your point though that for some edge cases returning all resource identifier objects for related resources may be too expensive. This may be the case for 1) to-many relationships, 2) having many related resources, which cannot be queried in an efficient way, and 3) adding or removing a related resource causes side-effects on other resources being added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the goal is to handle client-side caches, then the response structure should be much more flexible. It should be possible to return type/id entries of any resource type, as well as full resource objects of any type. The choice to only return type/id entries that occurred in the request seems arbitrary and not very practical.
Please prove me wrong and show that it's not useless, by providing a real-world compelling example where this makes sense. I haven't found any.
Use camelCase for fields to follow latest conventions. Co-authored-by: Jeldrik Hanschke <jelhan@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for addressing all open topics. Looks good to me now.
This PR applies consistent language and ordering across success (2xx) responses for creating, updating, and deleting resources and relationships.
It clarifies that it is always ok to return a 200 (or 201 in the case of creation) response with a document that does not include primary data when the server does not change the targeted resource(s) / relationship(s) in any way different from what was requested. This allows the server to include other top-level members, such as
meta
, in responses without primary data.This also removes a logical inconsistency in which MUST was used for 204 responses which contradicted a MAY allowance for 200/201 responses with documents that include
meta
.Closes #1540 #1581