Skip to content

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

Merged
merged 5 commits into from
Jul 12, 2022

Conversation

dgeb
Copy link
Member

@dgeb dgeb commented Jun 27, 2022

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

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.
@dgeb dgeb added this to the v1.1 milestone Jun 27, 2022
Copy link
Contributor

@jelhan jelhan left a 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.

Comment on lines 1569 to 1573
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.
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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?

Comment on lines 1583 to 1587
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.
Copy link
Contributor

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.

Comment on lines 1572 to 1573
includes a [Client-Generated ID](#crud-creating-client-ids)). Other top-level
members, such as [meta], could be included in the response document.
Copy link
Contributor

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 objects
  • meta: a [meta object][meta] that contains non-standard
    meta-information.

Copy link
Member Author

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.

Copy link
Contributor

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:

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.

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.

Copy link
Member Author

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.

@@ -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)
Copy link
Contributor

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).

Copy link
Member Author

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?

Copy link
Contributor

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.

Copy link
Member Author

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.

Comment on lines 1975 to 1977
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.
Copy link
Contributor

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:

  1. Response with all resources currently associated to that relationship.
  2. 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.

Copy link

@bkoelman bkoelman Dec 7, 2023

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.

Copy link
Contributor

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.

Copy link

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>
@dgeb dgeb requested a review from jelhan July 12, 2022 13:28
Copy link
Contributor

@jelhan jelhan left a 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy