Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RESTful design of a social network

I'm trying to wrap my head around RESTful design, and I'd like to understand it in terms of a social network. I've read REST API Design Rulebook, looked in some other books and read a lot of online resources. Still, when applying the rules to real-world problems I realize that I don't fully understand everything. I want to know if I have the correct understanding of REST by specifying some issues:

Hierarchy vs. flat design

Let's assume that I identify a particular user with

/users/42

Now, the photos uploaded by that user would end up in

/users/42/photos

and if he/she tags his/her friends in the photo those tags would end up in

/users/42/photos/1337/tags

But this provides no way of finding all photos where a particular user is tagged. Should I come up with a different hierarchy for that? It seems a bit awkward. Am I allowed to completely disregard the hierarchy and provide photos in combination with queries like this?

/photos?owner=42
/photos?tagged=42

Caching when content differs for different users

A Web Service should be designed to provide cacheable data (so that the client could decide to use the local copy, if it thinks that nothing has been changed), but how does that affect the privacy settings for different users? Two users, both logged in, might have the rights to view different information about e.g. user 42. Does that mean that I somehow need to request different URIs for different users accessing the profile information of the same user, or will the caching not be an issue as long as the users provide different credentials?

Providing both HTML and JSON/XML from the same resource

The book I mentioned specifies the rule that an API should be accessible at a subdomain starting with api, in their example http://api.soccer.restapi.org. I had planned to use the same controller for both user access and machine access (for example a mobile app). The controller would decide on which view to provide (text/html, application/json or application/xml) through the Accept field in the HTTP request header. I take it that that would be a bad idea for some reason (since a user would expect to see the subdomain www, not api), but I don't understand why. Can www and api point to the same server, or should I really try to move the HTML view to a different virtual host? Why?

I believe Ruby on Rails will (Convention over Configuration) provide both HTML and JSON from the same controller, thus sharing my idea that HTML and JSON are just different representations of the same data.

In other words, my book says that a certain resource should have one and only one URI, and that different representations should be provided based on the Accept field. Redirecting the user between different subdomain would violate the rule about providing any representation from the same resource, and duplicating the information (i.e. pointing two subdomains to the same virtual host) violates the rule about not providing multiple URIs for the same resource. Not providing the api subdomain violates yet another design rule. How can I solve this without violating any rule?

Limiting the data sent back

The query component should be used for pagination, but am I allowed to refuse to honor listing requests lacking search criteria and limit the number of items, without violating REST? I want to both reduce the database load and avoid having someone map the entire directory of users. I want

/users

to be an illegal request, while

/users?name=leet+hacker

would be valid but only return e.g. 100 items.

I also wonder if it's legal to return a subset of the database columns and more/all of them only if they're specifically requested using a query.

Controllers providing redundant data

I believe it's legal to provide a controller like

/users/me

but should it provide the exact same information as the document URI

/users/42

or should it redirect to it?

Extended rights for some users

Which is the RESTful way to provide additional functionality, for example administration rights? I'm now assuming that an administrator (of a photo, of a group of users or of the entire site) will be able to see more information about a particular object than other users. Should that information be kept at exactly the same URI and be sent automatically to the administrator but not to anyone else, should it be stored at a different location, should it be requested using a certain administrator query or provided in some other way?

Localization and settings updates

Although the majority of strings visible to the user should be provided by the view, there are some design decisions that might involve the API. The most obvious are names. Social networks sometimes allow the user to enter different names that are to be displayed in different language contexts. Names in some languages, like Russian and Arabic, are not easily transcribed automatically. In other languages, like Chinese, the local and international names can be completely different, with no resemblance or connection at all. What would be the RESTful way of handling this? I have a feeling that the answer will be the Accept-Language field, but is anyone seriously considering that for switching languages on the social network they belong to? Should all this information be returned to the caller every time, or can I rely on the settings? Will that work with a cache?

like image 347
Anders Sjöqvist Avatar asked Feb 16 '12 16:02

Anders Sjöqvist


People also ask

What is a RESTful design?

REST or RESTful API design (Representational State Transfer) is designed to take advantage of existing protocols. While REST can be used over nearly any protocol, it usually takes advantage of HTTP when used for Web APIs.

What is an API in social networks?

Application Programming Interface (API) is a platform provided by the social networks allowing other applications and websites to pull the social media data and integrate with their site or application.

What is RESTful pattern?

Representational state transfer (REST) is an architectural design pattern for APIs. APIs that follow this pattern are called REST APIs or RESTful APIs. REST sets certain standards between computer systems on the web that make it easier for systems to communicate with each other.


1 Answers

As @Mark Dickinson mentioned, there are a lot of questions here, which really should be separate, but I'll do my best.

Hierarchy vs. flat design

There is nothing in REST suggesting you cannot have multiple parallel hierarchies (though I understand that doing so with Rails is awkward). Having /users/42/photos/owner and /photos/owner/42 containing the same set is fine. Similarly /users/42/photos/tagged and /photos/tagged/42 can contain the same set. However, you shouldn't be worried about the URIs at this time. There is an excellent article A RESTful Hypermedia API in Three Easy Steps that describes how to design your API. In this article, the URIs are decided as the last step.

Also, using the HATEOAS contstraint, these various URIs should be discovered at runtime by the client, through the links and forms provided by your application.

Caching when content differs for different users

If you are going to serve different content from the same URL, then it's not going to be cacheable. You can partition you site into two types of content, public and personalised. The public content should be the same for everyone and can be cached. The personalised content is different for each user, which means that the amount you can cache it will be dramatically reduced (reduced to zero using the URL format you have used in your examples).

To get at least a small amount of caching on personalised content, partition the content by the user, so that for a give user you'll get some cache hits. For example, instead of having /users/42 that everyone can access, use /<UID>/users/42 where is the userid of the requesting user. e.g. user 234 would access the profile page for user 42 using the URI /234/users/42. For anonymous users you could either remove the /<UID part or use a specific userid for them, such as /public/users/42.

Providing both HTML and JSON/XML from the same resource

Use the Accept header. That is what it is there for.

Limiting the data sent back

You don't need to make /users an illegal request. Treat is a a collection and return a paginated list of users that the requester is permitted to see. For example for an anonymous request you might provide an empty list (or 204 No Content)

<users/>

and for a particular logged in user, you might provide their friends.

<users>
    <user id="42" href="/users/42" name="John Doe"/>
    <user id="53" href="/users/53" name="Jane Doe"/>
    ...
    <next href="/users?page=2"/>
</users>

when that user then GETs /users?page=2 you would then provide the next page of results

<users>
    <user id="69" href="/users/69" name="John Smo"/>
    <user id="84" href="/users/84" name="Jane Smo"/>
    ...
    <next href="/users?page=3"/>
    <prev href="/users"/>
</users>

With the last page of results providing no next link. To add a searching capability, you just add an appropriate form as part of the response.

<users>
    <user id="69" href="/users/69" name="John Smo"/>
    <user id="84" href="/users/84" name="Jane Smo"/>
    ...
    <next href="/users?page=2"/>
    <prev href="/users"/>
    <search href="/users" method="get">
        <name cardinality="required" type="regex"/>
    </search>
</users>

The results of the search would be paginated, just like the /users list. e.g., searching for leet hacker (Assuming you have permission to view a lot of leet hackers in the system) would produce something like

<users>
    <user id="234" href="/users/234" name="leet hacker"/>
    <user id="999" href="/users/999" name="leet hacker"/>
    ...
    <next href="/users?name=leet+hacker&page=2"/>
    <search href="/users" method="get">
        <name cardinality="required" type="regex"/>
    </search>
</users>

however you will probably need to provide more details in the user element, so the leet hackers can be differentiated.

Controllers providing redundant data

Both are acceptable. However as above (for caching reasons) I would use /<UID>/users/42, in which case you might want to redirect /42/users/me to /42/users/42.

This also helps from an analytics point of view as you might want to separately track a users access the their own page, from users access to other users page, to find out which users are popular and which are narcissistic. You may even find a correlation between the two :)

Extended rights for some users

Provide the admin links and forms in the appropriate response. For instance, accessing details of someone else's image might provide

<image id="266" href="/photos/266" caption="leet hacker with computer"
       src="http://us.123rf.com/400wm/400/400/creatista/creatista0911/creatista091100003/5827629.jpg"/>
    <tagged-users>
        <user id="234" href="/users/234" name="leet hacker"/>
    </tagged-users>
    <owner id="234" href="/users/234" name="leet hacker"/>
</image>

but for the image owner, it might provide

<image id="266" href="/photos/266" caption="leet hacker with computer"
       src="http://us.123rf.com/400wm/400/400/creatista/creatista0911/creatista091100003/5827629.jpg"/>
    <tagged-users>
        <tagged-user id="234" href="/users/234" name="leet hacker">
            <delete href="/photos/266/tagged/234" method="delete"/> 
        </tagged-user>
        <add href="/photos/266" method="put">
            <user cardinality="required" type="user-id"/>
        </add>
    </tagged-users>
    <owner id="234" href="/users/234" name="leet hacker"/>
    <delete href="/photos/266" method="delete"/>
    <update href="/photos/266" method="put">
        <caption cardinality="optional" type="string"/>
    </update>
</image>

Localization and settings updates

Use Accept-Language for the default language, but allow a user to change the language in their settings.

like image 67
Tom Howard Avatar answered Oct 20 '22 00:10

Tom Howard