I've read that Get requests should be idempotent.
I'm making an android app with a list of articles. Both guest and authenticated users can view a list of articles, but authenticated also get favorited status.
To make a request idempotent, the authenticated user should request both /articles and a second request to get the favorite status of this article.
How professional developers make these things? What is the best practice?
I see 3 ways:
Return a combined result based on the user. for guests favorited: 0, for authenticated if favorited, favorited: 1
GET /articles (statefull)
[
{
"id":1,
"title":"First Article",
"favorited":1
},
{
"id":2,
"title":"Second Article",
"favorited":0
},
{
"id":3,
"title":"Third Article",
"favorited":1
}
]
Return stateless and make ​additional request to check the favorited status for this article ids if authenticated.
GET /articles (stateless)
[
{
"id":1,
"title":"First Article"
},
{
"id":2,
"title":"Second Article"
},
{
"id":3,
"title":"Third Article"
}
]
if authenticated get favorite statuses for article id 1, 2 and 3
GET /favorites?id=1,2,3
[
{
"id":1,
"favorited":1
},
{
"id":2,
"favorited":0
},
{
"id":3,
"favorited":1
}
]
Return stateless. After login user need to request endpoint to get all favorited ids, save them in the local client, and on every item display check from local if post id is favorite. Note some users have 300+ favorited articles
After login get all favorited ids, save in client.
GET /myFavoriteArticleIds
[
1,
3,
5,
9,
17
]
And then make stateless requests
GET /articles (stateless)
[
{
"id":1,
"title":"First Article"
},
{
"id":2,
"title":"Second Article"
},
{
"id":3,
"title":"Third Article"
}
]
You should be using only one API request GET /api/articles. Authorized requests should have the Authorization header value. Based on that the response could be filtered while serializing the result object.
We can have customized responses from API based on the Authorization header.
Refer to Baeldung - https://www.baeldung.com/jackson-serialize-field-custom-criteria
It would perhaps help to review the definitions of safe and idempotent as found in RFC 7231. The semantics of GET are safe, which means that all of the constraints of idempotent are satisfied, and some others.
In summary, safe means read-only.
But it doesn't tell you anything about representations, or resource design.
How professional developers make this things? What is best practice?
Think about how you would design a web site? One answer would be to have a page for anonymous users, and then a different page for administrators (which includes the extra information that the administrators need). The second page would be locked down so that only authorized users can get at it (which has some interesting implications for caching).
The same basic principles hold for an API.
To distinguish the difference between guest and authenticated cases, I would recommend you to use namespaces for all APIs.
Ex:
For authenticated users,
/api/articles - list of articles along with favorites
/api/articles/$article_id - single article information along with favorited time etc
For guest users,
/guest_api/articles - only the list of articles
/guest_api/articles/$article_id - only the article information
Based on the user type, the favorited flag can be returned in response.
User type can be passed a query parameter.
Example: GET /api/v1/articles?user=guest
Related
Is there any way to order the subscription list of Youtube channels by recently subscribed?
youtube
.subscriptions()
.list("snippet")
.setOrder("")// relevance, unread, alphabetical
.setMaxResults((long) 1000) // it is not affecting, the max limit is 50
.setMine(true)
.execute();
According to documents, I can only get max 50 items at a time, and I have only three order type parameters relevance, unread, alphabetical.
But I need to reach the channel I subscribed lastly. I would be really appreciated it if anybody helps me to handle this.
Thanks in advance!
According to the docs, you have the following parameter at your disposal:
myRecentSubscribers (boolean)
This parameter can only be used in a properly authorized request. Set this parameter's value to true to retrieve a feed of the subscribers of the authenticated user in reverse chronological order (newest first).
Note that this parameter only supports retrieval of the most recent 1000 subscribers to the authenticated user's channel. To retrieve a complete list of subscribers, use the mySubscribers parameter. That parameter, which does not return subscribers in a particular order, does not limit the number of subscribers that can be retrieved.
That is: do insert something like .setMyRecentSubscribers(true) in the sequence of setters of your code above. (Also you may remove the setChannelId setter call, since, by requiring from you to be authorized to invoke this endpoint, the API already knows the channel to which your call is referring to.)
Note also that the parameter's maxResults maximum value is 50. To receive only the most recent subscriber have .setMaxResults(1) in the setter sequence above.
If your want to obtain the list of all your subscriptions then there's the following parameter:
mine (boolean)
This parameter can only be used in a properly authorized request. Set this parameter's value to true to retrieve a feed of the authenticated user's subscriptions.
Have .setMine(true) (without .setChannelId) in your setters sequence.
You will have to invoke repeatedly the API's endpoint to get all of your subscriptions, since this endpoint provides paginated result sets. Upon obtaining all those subscriptions, sort them by snippet.publishedAt.
If you're only interested to obtain the most recent channel to which to have subscribed, instead of the sort algorithm, is sufficient to use the max algorithm (O(n) instead of O(n log n)) on the same property.
For an example of how to implement pagination in your code, have a look at some of the sample code provided by Google itself.
As I understand from your question, you want to check if you are following a specific youtube channel with help of Youtube Data API V3.
For that it is mentioned in the document that you can use forChannelId parameter.
Also Youtube Data API has a playground to let you see the results of your query. You can simply put a channelId in forChannelId field and result will return an empty array if you are not subscribed specified channel or result will return the data of that specified channel if you are subscribed to it.
You can do a simple request from your Java app to get results. In this code example I'm checking if authorized youtube API user is subscribed to Firebase Youtube Channel or not.
SubscriptionListResponse response = request.setForChannelId("UC_x5XG1OV2P6uZZ5FSM9Ttw")
.setMine(true)
.execute();
And response will include details of specified channel in the request you will make. I also share response of the request I shared above.
{
"kind": "youtube#SubscriptionListResponse",
"etag": "zCQ7lTwIBgdyVsQmbymEu-fUgjU",
"pageInfo": {
"totalResults": 1,
"resultsPerPage": 5
},
"items": [
{
"kind": "youtube#subscription",
"etag": "A-G_B0BnSqn7XtJi7BgHJEk9L3Q",
"id": "uTEDDg6jpPBwnsim9moHkataEljshwFopudOgIy34nk",
"snippet": {
"publishedAt": "2020-07-08T14:02:43.789000Z",
"title": "Google Developers",
"description": "The Google Developers channel features talks from events, educational series, best practices, tips, and the latest updates across our products and platforms.",
"resourceId": {
"kind": "youtube#channel",
"channelId": "UC_x5XG1OV2P6uZZ5FSM9Ttw"
},
"channelId": "UCC77fYySvfP7p-6QGaa-3lw",
"thumbnails": {
"default": {
"url": "https://yt3.ggpht.com/-Fgp8KFpgQqE/AAAAAAAAAAI/AAAAAAAAAAA/Wyh1vV5Up0I/s88-c-k-no-mo-rj-c0xffffff/photo.jpg"
},
"medium": {
"url": "https://yt3.ggpht.com/-Fgp8KFpgQqE/AAAAAAAAAAI/AAAAAAAAAAA/Wyh1vV5Up0I/s240-c-k-no-mo-rj-c0xffffff/photo.jpg"
},
"high": {
"url": "https://yt3.ggpht.com/-Fgp8KFpgQqE/AAAAAAAAAAI/AAAAAAAAAAA/Wyh1vV5Up0I/s800-c-k-no-mo-rj-c0xffffff/photo.jpg"
}
}
}
}
]
}
In real time you can't do a single query across multiple rootnodes, so I was wondering if doing multiple queries (nested one after the other) is a bad practice? I understand this can be done in one query in firestore, but I am specifically using realtime for this portion of my app due to high number of user read/writes.
reference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
//get Data from query one here
reference2.addListenerForSingleValueEvent(){
//get Data from query two here
reference3.addListenerForSingleValueEvent(){
//get Data from query three here
}
}
}
The alternative to doing multiple calls to read the corresponding entries from each reference, is to duplicate the data of the other entries under each reference.
It's a bit hard to reason in your abstract example, so let's pick a more concrete, simple use-case: a chat app. Say you have two-level entities: users, and chat messages. Each chat message is written by a user, and a user has a display name. In the most normalized data model this could be:
users: {
user1: {
name: "david s."
},
user2: {
name: "Doug Stevenson"
},
usere: {
name: "Frank van Puffelen"
}
},
messages: {
message1: {
message: "In real time you can't do a single query across multiple...",
uid: "user1"
},
message2: {
message: "In a very general sense, nested callbacks...",
uid: "user2"
}
message1: {
message: "The alternative to doing multiple calls...",
uid: "user3"
}
}
Now let's say that we have a use-case where we want to display the latest 10 chat messages, each with the name of the user who posted that message.
The above data works fine, but you will need to read the user names from the /users node, pretty much how you're doing now. This is known as a client-side join, and is quite efficient since Firebase pipelines the requests over a single connection.
You'd could deduplicate the lookup of the names using a cache, since the user names typically change much less frequently than the chat messages. This reduced the overhead since you're only loading each user's data once, but the code can get a bit verbose.
An alternative is to duplicate the minimal data that you need for your use-case. This means your data model would look like this:
users: {
user1: {
name: "david s."
},
user2: {
name: "Doug Stevenson"
},
usere: {
name: "Frank van Puffelen"
}
},
messages: {
message1: {
message: "In real time you can't do a single query across multiple...",
name: "david s.",
uid: "user1"
},
message2: {
message: "In a very general sense, nested callbacks...",
name: "Doug Stevenson",
uid: "user2"
}
message1: {
message: "The alternative to doing multiple calls...",
name: "Frank van Puffelen",
uid: "user3"
}
}
Now you can display the list of the latest 10 messages with a single read. The cost is that you use more storage, but typically storage should be considered cheap. The disadvantage in code is that you now need to write the duplicate data, which is more complex. And of course the duplicated data could get out of sync.
All above approaches are valid. Which one you pick depends on the use-cases of your app, your comfort level in duplicating data, and how much you personally value things like the amount of code, bandwidth consumption, etc. There is no one-size-fits-all answer, so you will have to make your own call.
For some great reading/viewing on the topic, see:
NoSQL data modeling
Firebase for SQL developers
How to write denormalized data in Firebase
Firebase data structure and url
some more of my answers on NoSQL questions
In a very general sense, nested callbacks is considered poor programming style. Casually speaking, it's called callback hell. As you get deeper into these callbacks, the code gets increasingly difficult to read and manage.
That said, if it works for you, go for it. You're the boss of your code. If it doesn't work for you, you can do searches to find strategies for avoiding this situation.
I'd like to identify users beyond their username. Is there any way to identify users with a token/code/ID# that is static and will never change?
Simply because usernames are flexible and can be changed at any time meaning I cannot keep track users.
Dani you can track users by their user id, which stays static even if they change their username.
In Instagram's API example they use this endpoint: https://api.instagram.com/v1/users/{user-id}/?access_token=ACCESS-TOKEN
which gives the following response:
{
"data": {
"id": "1574083",
"username": "snoopdogg",
"full_name": "Snoop Dogg",
"profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1574083_75sq_1295469061.jpg",
"bio": "This is my bio",
"website": "http://snoopdogg.com",
"counts": {
"media": 1320,
"follows": 420,
"followed_by": 3410
}
}
Depending on what data or which endpoints you are hitting beforehand, you should be able to get to the user ID and use that accordingly throughout your application.
I am trying to get only sport type facebook events.
I am retreiving my events using the query below, but I want to filter these out by category. I have tried nested fields but I can't get the ones I need, I get everything back.
search?q=Brasov&type=event&fields=name,start_time,id,cover,owner
The events I am looking for have this category in the owner field.
"category": "Sports/recreation/activities",
"category_list": [
{
"id": "186982054657561",
"name": "Sports & Recreation"
}
Do you know any way I can get only the events that have this category_list id ?
That is not currently possible. I would suggest you to store the events you received from the API call and do a filter on your server (or client).
I have a json file on server:
{"images":[
{"url":"...", "likes":"123"},
{"url":"...", "likes":"234"},
{"url":"...", "likes":"345"}
]}
I get the json file on android read it, but if someone likes a picture i want to change the value of the first picture from 123 to 124, is this possible and how can i do this?
The whole point is to change a json value on server,from client side.
Also if this isn't possible how can i make this happen?
Also if i want to get the Top50 rated pictures,how can i sort them and get only the 50 picture,without getting all pictures and sorting them on android ?
Which one is better,initializing the Top50 images,when the user starts the app,or when the user click on the button Top50.I assume that if its when he click the button,there might be some performance issues server side?
My other idea is to have a function server side,which every 10 min,executes automatically and gets the Top50 rated and makes something like json file.So it all happens server side automatically.
To make this happen, client should expose some interface, i.e. function that will allow to modify file on server side. The interface and implementation of this function greatly depends on server itself, i.e. which protocols it handles, what built-in or external modules it supports, which languages are supported, etc... For example, the classic scenario is using apache as HTTP server, CGI enabled, and write CGI function in perl. So, in this case interface would look like http://server.name/like.cgi?image=image123.
How to modify the values on the server ?
For this every like of a photo should be a post request of this sort.
{
"data": [
{
"image_id": 3133456,
"likes": 343
},
{
"image_id": 3133456,
"likes": 343
}
]
}
On parsing this request server updates the corresponding image's like on the server.
How to get the top 50 rated/liked images from the server ?
Again you send a get request to such a url
http://server.getsomething.com/getTop50Images
On server side
On receiving such a request you make a query on the table in your database something like this
select image_id , image_url, likes from image_table limit 50 ORDER BY likes ASC
Now getting those query results and returning them as a json would not be a performance hit until you have huge bulk of data. like some million rows may be in your database.
Response can be something like this
{
"result": [
{
"image_id": 3133456,
"likes": 34400,
"url": "http://flickr.com/someimage"
},
{
"image_id": 3133456,
"likes": 34380,
"url": "http://flickr.com/someimage"
}
]
}
You still avoid using a database yourself but can lease it from clouds services like parse.
However if you won't be using those services then you can take a look at ftp packages for js. Like the neo JavaScript library by apache.
But still a good choice will be to go with the database approach (is quiet simpler).