1

Sorry upfront if this is duplicate, but I was unable to find topic with answers to my questions.

I'm building an MEAN stack app, and I'm planning to create REST API on backend. Now I've stumbled on the following problem.

I have one resource /discussion, which contains list of users and posts itself. The resource roughly looks like this:

discussion = {
    "name": "discussionName",
    "users": [<arrayOfUsersThatArePartOfThisDiscussion>],
    "posts": [<arrayOfPostsForThisDiscussion>]
}

What I want to achieve is list all the discussions that user contributed to, so that I can present those to him. Now, what I don't understand is what is the most REST-full way to do this.

My thinking is:

  1. Maintain a list of discussions for the user. In that case, the user resource would roughly look like:

     user = {
       "id": "myId",
       "name": "MyName",
       "discussionIds": [<listOfDiscussionsImPartOf>]
     }
    

If this is my representation, what happens when user tries to post into discussion that he previously didn't post? My understanding is than in that case the POST /disucssions/:id/posts should be executed to create another post.

But then, who is updating user/:userId/discussionIds? Is that client or server duty? If it's server duty, how do I force cache invalidation for that resource (user/:userId/discussions)? so that I get fresh data on following GET user/:userId/discussions

  1. Don't maintain a list of discussions in user resource.

In this case, how do I filter discussions that user is part of? I was thinking of using query string with GET operation, but then again, how do I write query that will filter resource based on existence of provided param in it's array? For clarity purposes, I'm looking for something like:

GET api.something.com/discussions?where userId in discussion.usersIds

Hope you can answer my questions. Thanks in advance

  • I feel your first design is good. This only issue is the "cache invalidation"? If so... Do you need cache ? If you do please enlighten us on you caching implementation. It will help. – Paulo Sep 09 '21 at 22:12
  • 1
    Actually I don't have any caching mechanism in place and I'm not planning on using one until I wrap my head about REST-full principles :) I was thinking more of browser caching or any other caches that might be standing between client and server https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.10. Generally my question is if operation 1. = POST resourceA which should result in operation 2. = update of resource B, who is responsible for operation 2.? If server is responsible for that, then there must be a way for server to notify client to remove cache for that resource – Nemanja Ilic Sep 09 '21 at 22:33
  • You can control how the cache behave, in the back end. https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching With specific header in your request. – Paulo Sep 09 '21 at 22:46
  • Okay, I guess I can always use 'Cache-Control: no-store'. But let's say that in future I would like to use browsers and proxies to cache my requests. Then how would I notify them that the post request created one resource and updated one resource? Is it restful to use Location header for this task? What if PUT ends up changing more than one resource on backend? – Nemanja Ilic Sep 09 '21 at 22:52
  • Okay then I think you might find some answers here https://stackoverflow.com/questions/13291805/how-to-proper-design-a-restful-api-to-invalidate-a-cache – Paulo Sep 09 '21 at 22:58

1 Answers1

1

Maintain a list of discussions for the user.

Yup, that's fine.

what happens when user tries to post into discussion that he previously didn't post? My understanding is than in that case the POST /disucssions/:id/posts should be executed to create another post.

That's a choice that you get to make.

Think about how we do this sort of thing on the web. You want to send a question to stack overflow, so you load a form, and you type a bunch of information into that form, and you submit it. The outcome is that the web browser sends a POST request, and the request-target is...?

Well, on the web it's whatever target stack overflow put into the metadata of the form itself. You the user don't care where it goes, you just push the button. The browser doesn't care either - it just copies the meta data from the form into the HTTP request. So the authority that controls the form also gets to choose which resource should be the target.

So you could, if you wanted to, have all the forms get submitted to the same resource. Or you could have a separate resource for each kind of form. Or a separate resource for each kind of form for each web page.

But the really interesting option is to identify the most important web page that will be changed if the form submission is handled correctly, because HTTP has cache-invalidation rules that will prevent compliant caches from re-using responses that may have been changed by your request.

Alas, when successful form processing will modify multiple resources, then you have to choose (only one request-target per request).

Of course, form submission is just an example case: all unsafe requests (POST/PUT/PATCH etc) share the same cache-invalidation rules.

how do I write query that will filter resource based on existence of provided param in it's array?

You don't filter resources; you GET a resource whose representation is the list filtered by the constraint you want.

/user-discussions?userId=12345

That's a perfectly fine identifier for a resource with a representation that is a list of discussions relevant to user 12345. How it is that you obtain that representation is an implementation detail - the web doesn't care.

A common example would be to extract the userId from the query string and then to use it as a parameter in a prepared statement.

There's nothing magic about using a query string (although it is convenient on the web, because we can use forms to allow the user to provide a userId value); making the id part of the path would also be fine.

URI design (specifically, the origin-form of the URI) is about identifying the people who care about the spelling of your identifiers, and making trade offs among their different concerns (the concerns of the writers producing your documentation aren't quite the same as your operators who are reading web logs, and those aren't quite the same as customers reading through their browser history, and so on).

VoiceOfUnreason
  • 52,766
  • 5
  • 49
  • 91
  • This still doesn't answer my question. Your examples /user-discussions?userId=12345 implies that user-discussions would have userId property, while in the question, user-discussion would have a list of userIds that contributed to it and since the proposed solution couldn't apply? Am I correct? – Nemanja Ilic Sep 10 '21 at 20:12
  • 1
    Now that I re-read the answer seems like it contains the answer to my question. Thanks! – Nemanja Ilic Sep 12 '21 at 13:28