restful-like CRUD operation url pattern for nested model - http

Generally,the CRUD operation url pattern for model can be like this(Suppose the Model is Post):
new: /posts/new(get)
create:/posts/(post)
edit:/posts/edit(get)
update:/posts/1(put)
get:/posts/1(get)
However if there is a nested model "Comment".
And the association of the "Post" and "Comment" is one-many.
So what should the CURD operation url pattern like for comments ?
new: /posts/1/comments/new or /comments/new
create:?
edit:?
update:?
.......
What is the best practice?
Update:
It seems that the url for comment should be like this:
Get one comment for one post: /posts/1/comments/1
create: /posts/1/comments
update: /posts/1/comments/1
delete: /posts/1/comments/1
Now I am confused with the update and delete operation.
For update and delete: /posts/1/comments/1
SInce the comment id is specified,so I wonder if the /posts/1 inside the url is necessary?

I think the key is whether a comment is "contained" by the post resource. Remember that RESTful urls should be permalinks so under all of your scenarios, the end point to a specific comment(s) must not change. It sounds like it's containted so the url pattern can have the comment nested within the post. If that's not the case (e.g. a comment could move to another post which if nested would change the url) then you want a more flat structure with /comment/{id} urls referenced by the post resource).
The key is if it's a RESTful "Hypermedia API" then like the web it constantly links to the nested or referenced resources. It doesn't rely on the client necessarily understanding the REST pattern or special knowledge as to what end point holds the referenced or contained
resource.
http://blog.steveklabnik.com/posts/2012-02-23-rest-is-over
If a 'comment' is the resource(s) under a 'post' resource:
([httpVerb] /url)
get a post:
[get] /posts/{id}
body has a couple options - either it contains the full deep comments array
(depends on how much data, chat pattern)
{
id:xxx,
title:my post,
comments: [...]
}
... or it just contains the post resource with a url reference to the comments e.g.
{
id: xxx,
title: my post,
commentsUrl: /posts/xxx/comments
}
could also have an option like this (or other options to control depth):
[get] /posts/{id}?deep=true
get a collection of comments within a post:
[get] /posts/{id}/comments
returns 200 and an array of comments in the response body
create a comment for a post:
[post] /posts/{id}/comments
body contains json object to create
returns a 201 created
edit a comment under post:
[patch|post] /posts/{id}/comments/{id}
body contains json object with subset of fields/data to update
returns a 200
replace a post:
[put] /posts/{id}/comment/{id}
body contains json object to *replace*
returns a 200
If you have tons of comments per post, you could also consider a paging pattern:
{
id: xxx,
title: myPost,
pages:6,
commentsUrl:/posts/xxx/comments/page/1
}
then:
/posts/{id}/comments/pages/{pageNo}
{
nextPage: /posts/xxx/comments/2,
pages:7,
comments:
[ { ...,...,}]
}
each page would reference the next page, the page count and an array of comments for that page. If you went with a paging pattern then each comment in the array would have a reference url to the individual comment.
If you find yourself putting an action in the url, you're probably doing something wrong. Good read: http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http

You want to use the canonical URL for delete/update, which would be the complete one. More importantly, you shouldn't be exposing your Primary Key values which come from the Database as ids in the public api that is your restful URL space. These values could change in the future based on a database update, restore from backup, or anything else, depending on vendor. And you don't want an infrastructure change making all your URLs invalid if you can help it.
If you're using /posts/{postnum}/comments/{commentnum}, you can number the public comment ids starting from 1 for every post, making for shorter, nicer URLs.

Related

GMB - Removal of LocationState object in Business Information API

Google deprecated the old GMB API v4.9 account.locations.get endpoint, and replaced it with Business Information API v1 locations.get.
Code change that affects me is:
Removal of LocationState object. The existing fields have been moved into Metadata.
The new Metadata object does not return the attributes LocationState object contained before. The ones I'm interested in are:
isVerified
isPublished
isSuspended
isDisabled
isDisconnected
etc...
My question is:
How could I get this data without using deprecated endpoints?
Try Verification API getVoiceOfMerchantState
isVerified (verify),
isPublished (hasVoiceOfMerchant=true AND hasBusinessAuthority=true),
isSuspended (complyWithGuidelines),
isDuplicate (resolveOwnershipConflict).
isDisabled & isDisconnected have no equivalent in new API
As far as I can see, based on the link you have sent it is written:
Endpoint URL:
Endpoints for all business information, attributes, categories, chains and locations search are accessible at https://mybusinessbusinessinformation.googleapis.com/v1/ instead of https://mybusiness.googleapis.com/v4/
The path name for locations endpoints has changed from
accounts/accountId/locations/locationId to locations/locationId
Maybe it was better if you could provide the request uri in the previous version so we could help you more precisely. Anyhow, what I tested in the google playground is as follows:
open [https://developers.google.com/oauthplayground]
after setting your clientId and Authorisation stuff, in the Request URI write
https://mybusinessbusinessinformation.googleapis.com/v1/locations/XXXXX?readMask=storeCode,metadata,profile,serviceArea,labels,adWordsLocationExtensions
instead of XXXXX, write your locationId
you can write different readMask fields, The possible fields for readMask are:
play with different fields to check if you have your desired one or not readMask="storeCode,regularHours,name,languageCode,title,phoneNumbers,categories,storefrontAddress,websiteUri,regularHours,specialHours,serviceArea,labels,adWordsLocationExtensions,latlng,openInfo,metadata,profile,relationshipData,moreHours";
If above does not help you, in the link below I see that all metadata attribute of a location might be:
Click [here] (https://developers.google.com/my-business/reference/businessinformation/rest/v1/accounts.locations#Location.Metadata)

Firebase POST to Realtime Database adding an extra section

I'm trying to upload data into Firebase Realtime Database I have tried first doing a POST through the API this is my Query:
Method: POST
EndPoint: https://[PROJECT_ID].firebaseio.com/users/5Vzshkdlu8W3sDSZMt9bc9SyhiF8.json
Note I'm using my Project ID
Headers: 'Content-Type': 'application/json'
Body:
{
"name": "Rene Alas",
"correo": "[Email]",
"image": "[URL to my Image]",
"title": "Albo Aficionado",
"acceso": 0
}
I tried this on Postman but it gets me an additional part:
As you can see it added a middle part between my given ID and the Data which is Auto Assigned in this particular example:
MTLim4FiFfdeZP4yPZP
Any Ideas how I can put the information underneath the given ID so it doesn't auto assign that key?
Kind Regards?
HTTP semantics say that POST creates a new resource under the location, which is what Firebase does in your situation.
If you want to write a value you specify to the location and overwrite any existing data there, that'd be a call with the PUT method.
If you want the data you pass (on a key by key basis) to be combined with data already at the location, use the PATCH method that Martin mentions.
If you want to update that node, the HTTP request method is PATCH and not POST.
This can be used to update individual childs, see the examples.

Use PUT or PATCH verb when request is updating and deleting: Real life design

I've been trying to get the HTTP verbs right lately, however I have a doubt regarding using PUT, PATCH or even POST for the following scenario.
The front end part is sending the following JSON data:
{
name: "Spanish-01",
code: "ESP01",
students: [{
IdStudent: 1,
name: "Peter Parker"
},
{
IdStudent: 2
name: "Ben Reilly",
dirtyRemove: true
}]
}
The back end code will update the Class record (e.g name and code). However, it will also delete the students with flag dirtyRemove, and those live in another table called Student.
So what's the rule here? Since PUT and PATCH according to w3.org here is for updating an existing resource. In this case the back end is both updating and deleting at the same time?
Should I use PUT or PATCH or neither?
NOTE: Don't mind about the FE part, I minimized the scope in order to get a more straightforward example
How your resources are implemented internally using tables is an implementation detail. It doesn't matter.
That said, your example payload doesn't fit PUT (to remove a student, you would omit it). It might fit PATCH, if you properly label the payload with a content type describing what semantics you expect.
Nit: the HTTP spec is not a W3 document, and the version you're looking at is outdated.

Meteor: Single-Document Subscription

Whenever I encounter code snippets on the web, I see something like
Meteor.subscribe('posts', 'bob-smith');
The client can then display all posts of "bob-smith".
The subscription returns several documents.
What I need, in contrast, is a single-document subscription in order to show an article's body field. I would like to filter by (article) id:
Meteor.subscribe('articles', articleId);
But I got suspicious when I searched the web for similar examples: I cannot find even one single-document subscription example.
What is the reason for that? Why does nobody use single-document subscriptions?
Oh but people do!
This is not against any best practice that I know of.
For example, here is a code sample from the github repository of Telescope where you can see a publication for retrieving a single user based on his or her id.
Here is another one for retrieving a single post, and here is the subscription for it.
It is actually sane to subscribe only to the data that you need at a given moment in your app. If you are writing a single post page, you should make a single post publication/subscription for it, such as:
Meteor.publish('singleArticle', function (articleId) {
return Articles.find({_id: articleId});
});
// Then, from an iron-router route for example:
Meteor.subscribe('singleArticle', this.params.articleId);
A common pattern that uses a single document subscription is a parameterized route, ex: /posts/:_id - you'll see these in many iron:router answers here.

Clarification On Firebase Denormalization Blog Post

I just read the Firebase blog post titled Denormalizing Your Data Is Normal, and I have request for clarification.
I was with it until the Considerations paragraph. Specifically, the following:
"Modification of comments is easy: just set the value of the comment under /comments to the new content. For deletion, simply delete the comment from /comments — and whenever you come across a comment ID elsewhere in your code that doesn’t exist in /comments, you can assume it was deleted and proceed normally"
For modifications, why don't I have to modify the duplicate comments stored under /links and /users?
For deletions, am I correct in my understanding that once I delete a comment I have to have logic in all my read logic to cross-check /comments in case it was deleted?
Thanks!
The structure detailed in the blog post does not store duplicate comments. We store comments once under /comments then store the name of those comments under /links and /users. These function as pointers to the actual comment data.
Consider the example structure from the post...
{
users: {
user1: {
name: "Alice",
comments: {
comment1: true
}
},
},
comments: {
comment1: {
body: "This is awesome!",
author: "user1"
}
}
}
Note that the actual comment data is only stored once.
If we modify /comments/comment1, we don't need to update anything else because we only store the name of the comment under /links and /users, not the actual comment contents.
If we were to remove /comments/comment1, that would remove the only existence of the comment data. However, we still have these "dangling" references to comment1 under /users/user1/comments.
Imagine we delete /comments/comment1, when we try to load Alice's comments, we can look and see that comment1 doesn't exist anymore. Then our application can react accordingly by either a) deleting the reference or b) ignoring the reference and not trying to display the deleted comment.

Resources