Skip to content
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

JSON Patch equivalent structures #193

Closed
guillec opened this issue Feb 10, 2014 · 13 comments
Closed

JSON Patch equivalent structures #193

guillec opened this issue Feb 10, 2014 · 13 comments
Milestone

Comments

@guillec
Copy link
Contributor

guillec commented Feb 10, 2014

On issue #176 there is a discussion about how to possibly map JSON Patch paths.

I've added some examples here for further discussion.

How to replace the title of a post:

PATCH /posts/1

# JSON Patch Document:
[
  { "op": "replace", "path": "/posts/1/title", "value": "New Title" }
]

# JSON API resource:
{
  "posts": [{
    "id": "1",
    "title": "Old Title"
  }]
}

# JSON Patch equivalent structure:
{
  "posts": {
    "1": {
      "id": "1",
      "title": "The title"
    }
  }
}

How to remove comment 5:

PATCH /posts/1
 
# JSON Patch Document: 
[
  { "op": "remove", "path": "/posts/1/comments/5" }
]
 
# JSON API resource:
{
  "posts": [{
    "id": "1",
    "links": {
      "comments": [ "1", "3", "5", "7" ]
    }
  }]
}
 
# JSON Patch equivalent structure:
{
  "posts": {
    "1": {
      "id": "1",
      "links": {
        "comments": {
          "1": "1",
          "3": "3",
          "5": "5",
          "7": "7"
        }
      }
    }
  }
}

How to replace author type with a new value of "human":

PATCH /posts/1
 
# JSON Patch Document: 
[
  { "op": "replace", "path": "/posts/1/author/type", "value": "human" }
]
 
# JSON API resource:
{
  "posts": [{
    "id": "1",
    "title": "Rails is Omakase",
    "links": {
      "author": {
        "href": "http://example.com/people/17",
        "id": "17",
        "type": "people"
      }
    }
  }]
}

# JSON Patch equivalent structure: 
{
  "posts": {
    "1": {
      "id": "1",
      "links": {
        "author": {
          "href": "http://example.com/people/17",
          "id": "17",
          "type": "people"
        }
      }
    }
  }
}

How to replace author type with id of 1 to "not-dog":

PATCH /posts/1
 
# JSON Patch Document: 
[
  { "op": "replace", "path": "/posts/1/authors/1", "value": "not-dog" }
]
 
# JSON API resource:
{
  "posts": [
    {
      "id": "1",
      "title": "One Type Purr Author",
      "links": {
        "authors": [
          {
            "href": "http://example.com/people/9",
            "id": "9",
            "type": "people"
          },
          {
            "href": "http://example.com/cats/1",
            "id": "1",
            "type": "cats"
          }
        ]
      }
    }
  ]
}
 
# JSON Patch equivalent structure:
{
  "posts": {
    "1": {
      "id": "1",
      "links": {
        "authors": {
          "9": {
            "href": "http://example.com/people/9",
            "id": "9",
            "type": "people"
          },
          "1": {
            "href": "http://example.com/cats/1",
            "id": "1",
            "type": "cats"
          }
        }
      }
    }
  }
}

How to remove multiple posts:

 
PATCH /posts
 
# JSON Patch Document: 
[
  { "op": "remove", "path": "/posts/1" },
  { "op": "remove", "path": "/posts/2" },
  { "op": "remove", "path": "/posts/3" },
  { "op": "remove", "path": "/posts/4" }
]
 
# JSON API resource:
{
  "posts": [
    {
      "id": "1",
      "title": "One Type Purr Author",
    },
    {
      "id": "2",
      "title": "One Type Purr Author",
    },
    {
      "id": "3",
      "title": "One Type Purr Author",
    },
    {
      "id": "4",
      "title": "One Type Purr Author",
    }
  ]
}
 
# JSON Patch equivalent structure:
{
  "posts": {
    "1": {
      "id": "1",
      "title": "One Type Purr Author",
    },
    "2": {
      "id": "2",
      "title": "One Type Purr Author",
    },
    "3": {
      "id": "3",
      "title": "One Type Purr Author",
    },
    "4": {
      "id": "4",
      "title": "One Type Purr Author",
    }
  }
}
@dgeb
Copy link
Member

dgeb commented Feb 11, 2014

Thanks very much for working up these examples and expanding on our discussion!

I think of the "JSON Patch equivalent structures" as the "schema" of an API. It's possible to think of an entire API as if it's acting on a single JSON document. We should probably spell out this abstraction in the spec, since it applies to all requests (not just PATCHes).

The one thing I would change in your examples is that patch paths should be relative to request paths. If patch paths are considered to be relative, the request path + patch path can be combined to reference a particular entity. I really think the current spec has it wrong in making the patch paths absolute for the whole API, since that approach renders request paths redundant and therefore confusing.

Relative paths would allow us to replace the title of a post with:

PATCH /posts/1

# JSON Patch Document:
[
  { "op": "replace", "path": "/title", "value": "New Title" }
]

Instead of:

PATCH /posts/1

# JSON Patch Document:
[
  { "op": "replace", "path": "/posts/1/title", "value": "New Title" }
]

Similarly, a comment could be removed from a post with:

PATCH /posts/1

# JSON Patch Document: 
[
  { "op": "remove", "path": "/comments/5" }
]

Instead of:

PATCH /posts/1

# JSON Patch Document: 
[
  { "op": "remove", "path": "/posts/1/comments/5" }
]

By using relative paths, API implementers could choose to allow bulk operations at request paths of any depth, even including the root. This would make for a very flexible but complex API. Alternatively, an API might only allow very specific patch operations that modify single entities. Of course, these decisions should all be optional and left up to the needs of each API - but it's nice that the spec could encompass any level of complexity.

@MajorBreakfast
Copy link
Contributor

@dgeb Edit: I like it :)

Add To-Many Relationship:
PATCH /posts/1

[{ "op": "add", "path": "/comments/-", "value": "1" }]

(Is that minus correct?)

and

Change To-One Relationship:
PATCH /posts/1

[{ "op": "replace", "path": "/author", "value": "3" }]

Is that correct?

@dgeb
Copy link
Member

dgeb commented Feb 11, 2014

@MajorBreakfast those ops look good to me 👍

@MajorBreakfast
Copy link
Contributor

But the JSON patch is just for changing fields and relationships, right? We don't support the creation of documents.

@dgeb
Copy link
Member

dgeb commented Feb 11, 2014

Correct - that is the current status. As I see it, support for JSON PATCH could optionally be extended further by some APIs. In my opinion, this is the right approach for bulk operations. I think JSON API should have an opinion about these use cases.

@guillec
Copy link
Contributor Author

guillec commented Feb 12, 2014

❤️

It's possible to think of an entire API as if it's acting on a single JSON document. We should probably spell out this abstraction in the spec, since it applies to all requests (not just PATCHes).

@dgeb this approach makes a lot of sense to me.

You also mention in #176 of not including 'links' in the JSON Patch path, still believe in that? Is it purely for simplicity on the client end?

@MajorBreakfast
Copy link
Contributor

@guillec I've thought about having the "/links" in the URL. I think now that we should include it. It makes the URLs a bit longer but not by much. I think that leaving it out simply hides something that is not worth hiding. Also: We're defining the JSON patch equivalent structure. It's defined for the "links". I'm for specifying that it has to be in there.

@diosney
Copy link
Contributor

diosney commented Feb 13, 2014

How do you feel about #198?

Do you think this thread can obsolete it?

@guillec
Copy link
Contributor Author

guillec commented Feb 13, 2014

@MajorBreakfast yeah I am leaning towards requiring it, if the only reason is simplicity for the client. I feel that the more we change the response body from the JSON Patch equivalent the more confusion there will be.

@dgeb
Copy link
Member

dgeb commented Feb 16, 2014

My rationale in #176 for not including links in the JSON Patch paths was not really driven by client simplicity, but more for consistency with the way paths are expressed in URI Templates. The example that's given in the JSON API spec is "posts.comments".

Upon a little reflection, I now realize that was a false equivalency: there's a difference between a link and its associated linked resource. We need to be able to distinguish between the two with JSON Pointers. For example, one might want to delete a link from a post to its author without also deleting the author.

Bottom line: I believe links should be required in JSON Patch paths.

@guillec
Copy link
Contributor Author

guillec commented Feb 18, 2014

@dgeb ahhh ok thanks for the explanation!

👍 on the bottom line

@steveklabnik
Copy link
Contributor

Adding to 1.0 because #193 (comment)

@dgeb
Copy link
Member

dgeb commented Jul 5, 2014

Closed via #237 which adds a description of the "reference document" used to determine an API's recommended URL structure.

@dgeb dgeb closed this as completed Jul 5, 2014
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

No branches or pull requests

5 participants