RESTful API end point structure to save and favorite items

Viewed 197

I'm thinking about how to structure the end points of a RESTful API.

Problem description

  • There are recipes and users.
  • A user can save recipes and unsave them.
  • A user can favorite recipes that he has saved and defavorite them.
  • A user has to give saved recipes a name the first time he saves them. He can edit that name later on.
  • It should be possible to easily retrieve all saved recipes of a user, along with information regarding which recipes are his favorite and the names of the recipes.

Question

How to structure the back-end API end points?

Proposals

These are 4 proposals to structure it. Which one do you think is better? Do you have another proposal?

Proposal 1:

End point Method Meaning
users/{userId}/saved-recipes GET Retrieve all saved recipes of a user. The response also contains information about the names of these recipes and whether they're the favorite of the user.
recipes/{recipeId}/save POST Save a recipe.
recipes/{recipeId}/unsave POST Unsave a recipe.
recipes/{recipeId}/favorite POST Favorite a saved recipe.
recipes/{recipeId}/defavorite POST Defavorite a saved recipe.
recipes/{recipeId}/edit-name POST Edit the name of a saved recipe.

Proposal 2:

End point Method Meaning
users/{userId}/saved-recipes GET Retrieve all saved recipes of a user. The response also contains information about the names of these recipes and whether they're the favorite of the user.
recipes/{recipeId}/save POST Save a recipe.
recipes/{recipeId}/save DELETE Unsave a recipe.
recipes/{recipeId}/favorite POST Favorite a saved recipe.
recipes/{recipeId}/favorite DELETE Defavorite a saved recipe.
recipes/{recipeId}/edit-name POST Edit the name of a saved recipe.

Proposal 3 (my personal favorite):

End point Method Meaning
users/{userId}/saved-recipes GET Retrieve all saved recipes of a user. The response also contains information about the names of these recipes and whether they're the favorite of the user.
users/{userId}/saved-recipes POST Save a recipe.
users/{userId}/saved-recipes/{recipeId} DELETE Unsave a recipe.
users/{userId}/saved-recipes/{recipeId}/favorite POST Favorite a saved recipe.
users/{userId}/saved-recipes/{recipeId}/favorite DELETE Defavorite a saved recipe.
users/{userId}/saved-recipes/{recipeId}/edit-name POST Edit the name of a saved recipe.

Proposal 4:

End point Method Meaning
users/{userId}/saved-recipes GET Retrieve all saved recipes of a user. The response also contains information about the names of these recipes and whether they're the favorite of the user.
users/{userId}/saved-recipes POST Save a recipe.
users/{userId}/saved-recipes/{recipeId} DELETE Unsave a recipe.
users/{userId}/saved-recipes/{recipeId}/edit POST Single end point to change the information about the relation between a user and a saved recipe. You can favorite and defavorite a recipe and edit the name of it using this single end point by providing fields in the body of the request such as {favorite: true, name: "Saved 1"}. The provided fields will be updated accordingly.
1 Answers

How to structure the back-end API end points?

How would you do this on a web site?

You would probably have a bunch of web pages (documents). Information is retrieved from the server by fetching (GET) copies of web pages. Information is delivered to the server by suggesting edits to web pages.

For hypertext applications run in a web browser, edits come from submitting web forms -- we tend to use POST for everything. So each kind of edit would have its own web form with its own identifier that you could GET, and the form.action would be the identifier of the web page that is going to change when the form is processed.

(Yes, a single web page might be the target of several different edits from different forms.)

Designed this way, your "proposal 1" is really close to the right idea, with a different interpretation of the identifiers.

/users/1/saved-recipes

This is the identifier of a document describing the saved-recipes of user 1.

/users/1/saved-recipes/save
/users/1/saved-recipes/unsave
/users/1/saved-recipes/favorite
/users/1/saved-recipes/defavorite
/users/1/saved-recipes/edit-name

These are identifiers of documents containing the forms that you use to propose changes to /users/1/saved-recipes. In each case, when you submit the form, the resulting request uses /users/1/saved-recipes as the target-uri of the request.

In a case where you had a more sophisticated client, capable of managing local edits to documents, then you wouldn't need forms. You would GET /users/1/saved-recipes to copy the document into your client, make your local edits, and then use PUT /users/1/saved-recipes to suggest that the server replace its copy of the document with your edited copy.

An important idea here is that we don't use URI for actions, we already have a registry of standardized actions (GET, POST, PUT etc). URI identify the documents that the HTTP methods act upon - in other words, which document is the target of the request.

Related