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

Creating new relationship #1158

Open
runk opened this issue Apr 26, 2017 · 9 comments

Comments

Projects
None yet
7 participants
@runk
Copy link

commented Apr 26, 2017

I think this topic was discussed several times, but I cannot find an answer to my question. I'm quite new to JSON API, so excuse me for my ignorance.

Assume there's an article resource, article has many comments.

/articles/1 - article resource.
/articles/1/comments - article's comments, related resource.

How do I create a comment? Am I right that I have to do two calls?

First,

POST /articles/1/comments
{
  data: {
    "type": "comments",
    "attributes": {
      "message": "Hello there"
    }
  }
}

Assume comment resource just created gets id 2.

.. and then link it to an article:

POST /articles/1/relationships/comments
{
  data: [
    { "type": "comments", id: 2 }
  ]
}

This is not quite making sense to me. Why do I have to create a relationship if related resource sits "under" /articles/1 namespace? It makes sense to me to do it in case with many-to-many relationship, but why it is proposed for many-to-one?

Should I include a relationship block during comment creation, that points to a parent article? But again, why would I do it if I'm working within article's namespace /articles/1/comments?

Please point me to the right place in spec.

@beauby

This comment has been minimized.

Copy link
Contributor

commented Apr 26, 2017

The spec says

A resource can be created by sending a POST request to a URL that represents a collection of resources.

Your /articles/1/comments represents a collection of resources (namely that of comments for the article with id 1). All good so far.

Now the part you may be missing is that the spec never says "the created resource MUST have exactly the same attributes and linkage data as the creation payload". Some attributes/relationships may be decided by the server (such as here, where the article id is decided, from the route, by the server).

@jamesplease

This comment has been minimized.

Copy link
Contributor

commented Apr 26, 2017

How do I create a comment? Am I right that I have to do two calls?

The spec is pretty light on the behavior of the /resourceOne/:resourceOneId/resourceTwo endpoints. I only see it referenced for GET requests, so if I were to author an implementation of JSON API, I'm not sure if I'd add support for write endpoints.

The way that I perform this operation in one call is to just use the regular /comments endpoint. It's really nothing fancy, because a resource object can represent its attributes and its relationships.

POST /comments

{
  data: {
    type: 'comments'
    attributes: { ... },
    relationships: {
      article: {
        data: {
          type: 'articles'
          id: 1
        }
      }
    }
  }
}

So far I've only used endpoints of the form articles/1/comments as a handy shortcut for reading data. It doesn't seem too convenient for writing imo, given things like the ambiguity you mentioned around m-to-1 and m-to-n relationships (is it pointing to a list, or not?)

@olosegres

This comment has been minimized.

Copy link
Contributor

commented Jun 8, 2017

Actually, when you POST to /articles/1/comments server should create relation automatically, without need to POST /articles/1/relationships/comments.

But if you POST to /comments, you should add article's relationship to the body of request.

If some comment already exist on server, you may POST /articles/1/relationships/comments with it's id to create a relationship.

@hibaymj

This comment has been minimized.

Copy link

commented Jul 6, 2017

I see a couple things that stick out to me as odd here.

  1. The convention of /resource/{id}/{rel-name} is not mentioned anywhere as part of the spec that I can find, therefor it is a poor idea to assume the existence of a resource at that particular URL. It is fortunate it was not included in the spec, as this would have further crippled the hypermedia capabilities of json-api.

  2. I would agree with @jmeas that as defined in the spec, the back reference method of defining the relationship is best, however it is unintuitive and would require some form of mental gymnastics by the user to reason out.

A possible extension and potentially currently supported option which is less counter intuitive come to mind.

  • One solution would be to extend the specification by allowing POST or PATCH requests to the relationships link to contain a 'resource object' as well as a 'resource identifier object', following the same error handling rules as previously defined.

  • A less ideal, but I think currently supported method by the spec would be the POST or PATCH a compound document with a resource identifier object using a user defined ID to the relationships link with an included resource object.

@rintaun

This comment has been minimized.

Copy link

commented Sep 15, 2018

I realize this issue has been inactive for quite some time, but as I read the specification, nothing normative seems to be said regarding the structure of URLs.

For example, although URLs such as /articles/1/relationships/comments are included in a number of examples, the /relationships bit is not mentioned even once in the actual normative text of the specification.

If the URLs shown in the examples are meant to be normative, that needs to be explicitly stated; without such a statement, my personal opinion is that they must be considered non-normative. If the specification is intended to cover the structure of resource URLs, quite a significant amount of work will be necessary to make a robust specification for that structure -- personally, I believe that it should be considered beyond the scope of JSON:API.

Proceeding with the assumption that I am not wrong in my reading, most likely, any of the approaches discussed above would be acceptable under v1.0 of the specification (as well as the v1.1 draft as it exists as of this writing).

Of course, it is entirely possible that I'm wrong, and have missed something in my many readings of the spec. In that case, could someone please point me to where resource URL requirements are described?

@jamesplease

This comment has been minimized.

Copy link
Contributor

commented Sep 15, 2018

@rintaun

This comment has been minimized.

Copy link

commented Sep 15, 2018

@jamesplease Thank you :) I suppose that's what happens what you hyperfocus on one thing... I had completely skipped over the recommendations page.

@Alexandre-Carbenay

This comment has been minimized.

Copy link

commented Feb 22, 2019

@jamesplease what would be the response from the server to the request you gave as an example?

In particular, would that solution mean that there are two relationships endpoints a server must expose:

  • the expected one, from article to comments /articles/1/relationships/comments, containing the new relation created by the request
  • another one on the other side, from comment to article /comments/2/relationships/article

If this is the case, it would also mean that we can delete a relationship between two resources either from one or the other endpoint, right?

@jamesplease

This comment has been minimized.

Copy link
Contributor

commented Feb 22, 2019

@jamesplease what would be the response from the server to the request you gave as an example?

One part of using JSON:API that I like is that it is flexible, to a degree. In situations like this, you get to decide what sort of response makes sense to you. My rule of thumb is: come up with a solution that makes sense to me that doesn't go against the spec. And anything that might be surprising or unusual should be documented somewhere.

In the example I posted above, I can think of two possibilities (based on my memory of the spec. It's been awhile since I really dug into it tbqh):

  1. Return the created comment as data and the created article in included (both at the top-level)
  2. Return only the created comment as data, and allow a user to pass the includes query param to optionally return the newly-created article (docs)

In particular, would that solution mean that there are two relationships endpoints a server must expose:

What you're describing are back-links or two-way relationships, which I think is an independent topic from the one in this issue.

I don't recall the spec enforcing back-links, but APIs that support it are much more useful. If you're using a relational database under-the-hood, you'll likely need to store the relationships both ways (this guide from Fortune.js describes one approach).

If this is the case, it would also mean that we can delete a relationship between two resources either from one or the other endpoint, right?

Ideally, yup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.