Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should nested relationships be reflected in URLs for JSON API?

Tags:

json-api

I'm trying to follow JSON API. I need to expose CRUD access to a nested resource: product reviews.

Prior to using JSON API, I'd expect a REST interface like this:

GET    /products/:product_id/reviews     - list reviews for a product
POST   /products/:product_id/reviews     - add a review for a product
PATCH  /products/:product_id/reviews/:id - update a review for a product
DELETE /products/:product_id/reviews/:id - delete a review for a product

I see some mention of a nested structure like this in the spec:

For example, the URL for a photo’s comments will be:

/photos/1/comments

But I'm not sure whether this structure is intended for all actions.

On the one hand, POST /products/:product_id/reviews for creation seems redundant if I'm going to specify the product in the POST body, under the review data's relationships.

On the other hand, if it's useful to specify a product id when deleting a review (maybe it isn't), DELETE /products/:product_id/reviews/:id seems like the only sane way to do it; people argue about whether a request body is even allowed for DELETE requests.

I could nest for some requests and not others:

GET    /products/:product_id/reviews  - list reviews for a product
POST   /products/:product_id/reviews  - add a review for a product
PATCH  /reviews/:id                   - update a review
DELETE /reviews/:id                   - delete a review

But that seems weirdly inconsistent.

I could never nest:

GET    /reviews     - list reviews for the product specified in params
POST   /reviews     - add a review for the product specified in params
PATCH  /reviews/:id - update a review
DELETE /reviews/:id - delete a review

But that seems awkward, and doesn't seem to match the first quote I made from the docs.

Should nested resource relationships be reflected in the URL when using JSON API?

like image 937
Nathan Long Avatar asked Aug 08 '17 17:08

Nathan Long


People also ask

What is the purpose of link relation in REST API?

A link relation is a descriptive attribute attached to a hyperlink in order to define the type of the link, or the relationship between the source and destination resources. The attribute can be used by automated systems, or can be presented to a user in a different way.

What is JSON API specification?

JSON:API is a specification for how a client should request that resources be fetched or modified, and how a server should respond to those requests. JSON:API can be easily extended with extensions and profiles.

Which type of API uses JSON format?

JSON API is a format that works with HTTP. It delineates how clients should request or edit data from a server, and how the server should respond to said requests.

What is JSON API client?

JsonApi. Client is a set of extension methods to the HttpClient which allow for reading and writing of JSON:API documents.


2 Answers

If you coming from CQRS camp, you will understand why design Restful API sometimes awkward. It is awkward because naturally Query actions (GET) and Mutation actions (POST, PATCH, DELETE) should talk in two different languages. Query actions naturally relationship-oriented and data rich; while Mutation actions not. So it feel easy to use nested URL to traversal between relationship entities. But Mutation you should provide just enough information for tasks. Sometimes it is redundant like your Post example. Sometimes missing like your DELETE example. Sometimes you have a task involve many resources; you don't know where to put in.

You should check Facebook Graph API or Azure Graph API, they met same problems and have some good solutions. It's important that you should follow consistent design. Some rules are:

  • DELETE, UPDATE always to direct resource.
  • POST use with nested resource if you want create both object and main relationship. Secondary relationships should put in BODY. If you have two equal relationships, consider have both nested APIs.
  • Use POST against fake resource to for tasks involve with many resources.

    POST /transferfund

  • Use POST against fake relationship for tasks could not fit with any HTTP verbs. For example, you want have body for delete action, use

    POST /resource/id/deleteItForMe { reason: "I hate it"}

like image 171
Giap Nguyen Avatar answered Oct 25 '22 02:10

Giap Nguyen


I really like your question, since I have been having the same thoughts. I'm puzzled that no one has left an answer yet.

I have been using JSON API a little over a year on a production system and I would like to give my two cents.

At first when I started the project that was going to use JSON API, I was in doubt of nested vs non-nested resources. I then ran into issues with nested resources that would have been avoided with non-nested resources.

To take the same paths as in your example, consider the GET /products/:product_id/reviews endpoint. When this is made it make very much sense to nest a review under a product because we are initially showing reviews in context of a product. Everything is good.

We then later want to build a page in the frontend that shows a user and all the reviews that user has authored. Although we already have an endpoint for getting reviews, we will have to build a new one, e.g. GET /users/:id/reviews.

If we hade just put the first endpoint on GET /reviews with a filter of ?filter[product_id]=:id, we could just add a new filter to that endpoint, which makes much sense IMO.

I do use nested resources, but only for singleton resources like GET /users/:id/email_settings and a few other special cases where it makes sense.

In my experience, it makes it easier in the future if each resource is thought of as independent from other resources. There exists resources and relationships between resources. No resource "owns" another resource in the context of the API (in context of business-logic it's another story).

I have worked with this strategy and it still surprises me how well it works when adding new functionality to existing endpoints and when adding new endpoints.

like image 27
Lasse Skindstad Ebert Avatar answered Oct 25 '22 03:10

Lasse Skindstad Ebert