For a school project, I'm designing a RESTful API for managing restaurants and restaurant owners (restaurateurs)
There is a relation between restaurants and restaurateurs.
A restaurant can have one or zero restaurateurs and a restaurateur can have zero or more restaurants.
So far I have the following endpoints:
GET /api/restaurants
POST /api/restaurants
GET /api/restaurants/{id}
PUT /api/restaurants/{id}
DELETE /api/restaurants/{id}
GET /api/restaurants/{id}/restaurateur
GET /api/restaurateurs
POST /api/restaurateurs
GET /api/restaurateurs/{id}
PUT /api/restaurateurs/{id}
DELETE /api/restaurateurs/{id}
GET /api/restaurateurs/{id}/restaurants
GET /api/restaurateurs/{id}/restaurants/{restaurant-id}
Now I want to add the feature to link and unlink restaurants and restaurateurs
What I had in mind is this:
To link a restaurateur to a restaurant
POST /api/restaurants/{id}/restaurateur
Body: {"restaurateur_id": 99}
And
POST /api/restaurateurs/{id}/restaurants
Body: [99, 88, ...]
To unlink a restaurateur from a restaurant
DELETE /api/restaurants/{id}/restaurateur
And
DELETE /api/restaurateurs/{id}/restaurants/{restaurant-id}
Are those good RESTful solutions, and if not what would you recommend?
What are the best practices for this kind of problem?
If you REST API is for in-house applications, you can use the LINK and UNLINK HTTP verbs.
Related
I always have the doubt that you can see below when i need to create theresource URLs for a REST API. I wonder if some one can help me.
Let's suppose that i have two models.
User
Post
User can submit his own posts and can comment his own and another posts.
Main resources URLs for User would be:
GET /users # Retrieve all users.
POST /users # Create a new user.
GET/DELETE/PUT /users/{user_id} # Get, remove and update an user.
Main resource URLs for Post would be:
GET /posts # Retrieve all posts.
POST /posts # Create a new post.
GET/DELETE/PUT /posts/{post_id} # Get, remove and update a post.
My problem come when for example i want:
Top 10 submitters (filter for a parameter(external link, discussion, all)). The URL should be:
GET /users/top?type=ext
GET /users/top?type=disc
GET /users/top # for all
Or maybe it should be:
GET /users?top=ext
GET /users?top=disc
GET /users?top=all
The same but with posts:
Top 10 commented post (filter for a parameter(external link, discussion, all)). The URL should be:
GET /posts/comments?type=ext
GET /posts/comments?type=disc
GET /posts/comments # for all
Or maybe it should be:
GET /posts?top=ext
GET /posts?top=disc
GET /posts?top=all
Any of above options are good for you or it should be another way?
Regards
I like to think of the REST URI as a model representation in itself.
So /users/top doesn't make a lot of sense but /posts/comments seems to be fine (as comments could also be a different model). But for your case, I recommend other set of query parameters as they're widely used for filtering & sorting requests. So in your case, I'd recommend something like:
GET /users?sort=ext&order=desc&limit=10
which would help me understand that I'm requesting 10 user resources which have been sorted for ext in the descending order. (you can even change it to type=ext if you want)
As usual; REST doesn't care what spellings you use.
One place you might look for inspiration is... stack overflow itself. Do these URI look familiar?
/questions?sort=newest
/questions?sort=featured
/questions?sort=votes
The API has pretty decent documentation, which will also offer hints at decent spellings to deal with paging and search ranges.
That said, IMDB takes a different approach - The Shawshank Redemption uses a straight forward "I am an element of a collection" spelling
http://www.imdb.com/title/tt0111161/
But the top rated titles of all time? they appear as a chart
http://www.imdb.com/chart/top
But i want to know if there is a standard according to #Hawkes answer or there is no standard at all.
No standard at all; just local spelling conventions. Which is, to some degree, part of the point of REST: the server can use whatever spellings for URI make sense, and the client just "follows its nose" based on its understanding of the processing rules for the media type and the data provided by the server.
In my application , I am going to use connectwise API , but I can't figure out how to call their API, like
How to connect
How to pass header (json data)
How to authentication (I have company id , public and private key)
How to make call and take response
I am Python guy
Thanks in advance
This question is quite broad, and you'll need to get familiar with something such as the Requests module if you aren't already.
Also, ConnectWise has a lot of documents at their developer site and registration is free and easy.
However I went through this journey myself over the last few weeks and learnt a lot so I'll share it with you.
Authentication
Authentication is done by creating an API user. In CW Manage you can create a member user with the API license class.
In ConnectWise Manage:
Go to System => Members
Go to the API Members tab.
Create a new API member that gives the API access to the areas that you need it, such as corporate/territorial levels. Note the username, amd that you cannot specify a password. Complete all the mandatory fields (owner ID, system default, group, approvers, etc).
When you have made this user go to the API keys tab. Enter a description for your new set of keys, and save it. When you do this you'll see the public and private keys once (and once only). Note them down.
Authorisation Header
In Python:
>>> import base64
>>> base64.b64encode("a+b:c")
...where a is your ConnectWise company name (what you type in in the login box), b is your public key, and c is your private key. This will return something which will be what you use to access the system. Copy it.
Making your first request
Now in Python make a new file and put this in it (this is quick and dirty but it should get you started):
import requests
cwToken = ""
# This is the ConnectWise access code generated earlier
cwUrl = "https://api-eu.myconnectwise.net/v4_6_release/apis/3.0/"
# check the URL matches your region, look at your CW Manage login box if not
cwHeaders = {"Authorization":"Basic " + cwToken,
"clientID":"<insertyoursecretClientIDhere>",
"Content-Type":"application/json"}
# this is your authorisation payload
try:
r = requests.get(cwUrl + "company/companies?pageSize=1000&conditions=type/id==1", headers=cwHeaders)
# request has been made
r.raise_for_status()
except:
print(r.text)
raise
companies = r.json()
The companies object now contains a list of your first 1,000 clients (type is ID 1 -- client). I've included an example of the conditions string because it took me a while to work out what it is and how to use it. But just take it off if you don't want the server to do your filtering for you.
You'll then be able to modify the above, or turn it into a module (which is what I did) to then make quick and portable calls, something like
cw.getCompanies()
which would simply return you a JSON object with all your companies in.
Going further
Study the documentation!! It is comprehensive enough to get you started. And the forums are actively monitored by staff (although they help mostly with C# queries and not scripting).
Just an update for 2020 since Daniel's response was incredibly helpful, but is missing a change Connectwise made in 2019 that now requires ClientIDs in order to auth.
Make a client ID here (scroll to bottom, although it's quick and useful to read all of it):
https://developer.connectwise.com/ClientID#What_is_a_clientId.3F
Daniel's code should then look like:
cwHeaders = {"Authorization":"Basic " + cwToken,
"clientID":"<insertyoursecretClientIDhere>",
"Content-Type":"application/json"}
Hi all,
I'm new to REST and Web API. I'm a bit confused on how to design URIs for my resources.
Given that I have a domain with the following resources: Blogs, Posts and Users.
Blog (1) ------ (0..*) Post (0..*) ------ (1) User
A blog can have many posts and each post is associated with one blog. An user can have many posts and each post is associated with one user.
For Blog and User resources the URIs would be like the following:
GET /blogs - get list of all blogs
GET /blogs/{id} - get blog by id
POST /blogs - create new blog
PUT /blogs/{id} - update blog
DELETE /blogs/{id} - delete blog
GET /users- get list of all users
GET /users/{id} - get user by id
POST /users - create new user
PUT /users/{id} - update user
DELETE /users/{id} - delete user
But what about Posts resource? How to handle associations? I'm thinking of the following alternatives - which ones are correct and why?
-- Get all posts by blog
1. GET /blogs/{id}/posts
or
2. GET /posts?blogid={id}
-- Create new post in a blog
3. POST /blogs/{id}/posts
or
4. POST /posts (here I would then in the payload send the IDs of the resources this post is associated with. BlogId and UserId)
-- Get all posts by blog and by user
5. GET /blogs/{id}/posts?userid={id}
or
6. GET /posts?blogid={id}&userid={id}
If anyone could point me in the right direction here, I'd be grateful.
Since a post is always associated with a blog and a user ID, I would choose options 1, 3 and 5:
GET /blogs/{id}/posts
POST /blogs/{id}/posts
GET /blogs/{id}/posts?userid={id}
The first question you should ask yourself is how important it is to you that your API is truly RESTful? It's actually a lot more fiddly than it seems to achieve this.
Is your API going to be consumed only by your own software\organisation?
Will your API be accompanied by documentation?
If the answer to 1 or 2 above is true, then the value of being truly RESTful is questionable... it's all or nothing with REST so either you go the whole hog or you don't worry.
For an API to be a true REST API, it must be discoverable from a single entry point (see here: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven). Each call should return information on other related calls that can be made on that resource.. usually via links of some kind, this is one possible structure:
{
"Id" : 1,
"Identifier" : "123's First Blog",
"links" : [
{
"rel": "http://myapi/res/posts",
"href": "http://myapi/blog/1/posts"
},
{
"rel": "http://myapi/res/users",
"href": "http://myapi/user/123"
}
]
}
The rel is a link to a summary\definition of the resource, and the href should point to the api itself.
Anyways, the point of all this is that if you do want to be truly RESTful, then let the links between resources and uris dictate the design. Think about how you would discover the particulars for each call from a single starting point, and the structure should reveal itself much like software design through TDD.
If you don't need to be RESTful, then things become much simpler. Just design your API in the most natural way for you, your architecture, and your developers. If you document things properly, then this will lead to a much more efficient API and one that is quicker to develop against.
Mario's answer to this question is sound, and I would also favour those options over the others. I just thought you should know the whole story that accompanies such a desicion.
If this doesn't make sense or you would like more information then post a comment and I'll try to help :)
So I want to know what are the best practices for doing actions on a object that doesn't change the state of the object. If that doesn't make sense bear with me, I think the tweet example explains what I am trying to say.
I understand the basics like described here:
What are the best/common RESTful url verbs and actions?
And how it works when updating/getting/deleting etc an object. But what about actions that don't change the state of the object?
For example say we have a tweet object:
GET `/tweets (gets a list of tweets)
GET `/tweets/new (gets a new page to create a new tweet)
POST `/tweets (posts data to server to create new tweet)
GET `/tweets/:id (get a single tweet)
GET `/tweets/:id/edit (get a page to edit an exisiting tweet)
PUT `/tweets/:id (put data to server to edit exisiting tweet)
Delete `/tweets/:id (delete an exisiting tweet)
This makes sense to me. But how do i form the URL for reply/ follow / retweet/ favorite, some of which don't actually change the state of the tweet?
Should I do something like below?
POST `/tweets/:id/reply (post the reply message to the server)
POST `/tweets/:id/follow (post a boolean? yes I follow?)
POST `/tweets/:id/retweet (again post a boolean?)
POST `/tweets/:id/favorite (ditto)
Or do a
POST `/tweet/:id/actions (Do a post with the action I want to take as a parameter)
Or is there no "standard way".
Anyways thanks for the help!
Great question.
As always, it helps to switch the framing to nouns instead of verbs. What are the resources you're acting upon when you reply/etc.? And can those resources be fetched/addressed also?
In each of the cases you mentioned, I think the answer to the second question is yes. And in fact, Facebook's Graph API and GitHub's REST API both follow this approach.
E.g. for replying:
GET /tweets/:id/replies to get a list of tweets that were in reply to the given one
POST /tweets/:id/replies to create a new tweet in reply to the given one. The important part here is that success is a 201 Created w/ the Location header set to the created tweet's endpoint, e.g. /tweets/1234.
(Deleting a reply is then just deleting a tweet.)
Following/retweeting/favoriting are a bit trickier because the "nouns" are lightweight connections, and in fact, I just asked a Stack Overflow question for the "best" way to expose those:
RESTful API design: best way to CRUD lightweight connections?
You can see on that thread the specific way(s) you might implement following/retweeting/favoriting.
Hope this helps!
I'm doing research into a web API for my company, and it's starting to look like we might implement a RESTful one. I've read a couple of books about this now (O'Reilly's "RESTful web services" seeming the most useful) and have come up with the following set of URIs and operations for an object that can be commented on, tagged, and rated.
It doesn't really matter what the object is, as this scenario applies to many things on the net, but for the sake of argument lets say it's a movie.
Some of these seem to fit quite naturally, but others seem a bit forced (rating and tagging particularly) so does anybody have any suggestions about how these could be improved? I'll list them with the URI and then the supported verbs, and what I propose they would do.
/movies
GET = List movies
/movies/5
GET = Get movie 5
/movies/5/comments
GET = List comments on movie 5
POST = Create a new comment on movie 5
/movies/5/comments/8
GET = Get comment 8 on movie 5
POST = Reply to comment 8 on movie 5
PUT = Update comment 8 on movie 5
/movies/5/comments/8/flag
GET = Check whether the movies is flagged as inappropriate (404 if not)
PUT = Flag movie as inappropriate
/movies/5/rating
GET = Get the rating of the movie
POST = Add the user rating of the movie to the overall rating
Edit: My intention is that the movie object would contain its rating as a property, so I wouldn't really expect the GET method to be used here. The URI really exists so that the rating can be an individual resource that can be updated using the POST verb. I'm not sure if this is the best way of doing it, but I can't think of a better one
/movies/5/tags/tagname
GET = Check whether the movies is tagged with tagname (404 if not; but if it is tagged with the tag name should it return the actual tag resource by redirecting to something like /tags/tagname?)
PUT = Add tag tagname to the movie, creating the tag resource /tags/tagname if required
DELETE = Remove tag tagname from the movie, deleting the tag resource tags/tagname if nothing is tagged with it after this removal
Note that these wouldn't be the entire URIs, for example the URI to list the movies would support filtering, paging and sorting. For this I was planning on something like:
/movies/action;90s/rating,desc/20-40
Where:
action;90s is a semi-colon delimited set of filter criteria
rating,desc is the sort order and direction
20-40 is the range of item indices to get
Any comments about this API scheme too?
Edit #1
This post is getting quite long now! After reading some of the answers and comments, this is the changes from above I'm planning on making:
Tags will be handled as a group rather than individually, so they will be at:
/movies/5/tags
GET = List tags
POST = Union of specified tags and existing tags
PUT = Replace any current tags with specified tags
DELETE = Delete all tags
I'm still really not sure how to handle flagging a comment though. One option is that instead of POSTing to a comment replying to it, a comment object will include its parent so it can be POSTed to the general URI, i.e.
/movie/5/comment
POST = Create a new comment (which may be a reply to a comment)
I could then use the POST to a comment to flag it. But this still doesn't feel quite right.
/movie/5/comment/8
POST = Flag comment
Most of what you have looks good. There were just a couple of strange things I saw. When I put my URLs together, I try to follow these four principles.
Peel the onion
If you make the R in REST really be a resource then the resource URL should be able to be peeled back and still be meaningful. If it doesn't make sense you should rethink how to organize the resource. So in the case below, each makes sense. I am either looking at a specific item, or a collection of items.
/movies/horror/10/
/movies/horror/
/movies/
The following seems funny to me because flag isn't a resource, it's a property of the movie.
/movies/5/comments/8/flag -> Funny
/movies/5/comments/8/ -> Gives me all properties of comment including flag
Define the View
The last peice of the URL describes how to show the resource. The URL /movies/horror/ tells me I will have a collection of movies refined by horror. But there might be different ways I want to display that collection.
/movies/horror/simple
/movies/horror/expanded
The simple view might just be the title and an image. The expanded view would give a lot more information like description, synopsis, and ratings.
Helpers
After the resource has been limited and the proper view figured out, query string parameters are used to help the UI with the little stuff. The most common query string parameters I use are
p => Page
n => number of items to display
sortby => field to sort by
asc => sort ascending
So I could end up with a URL like
/movies/horror/default?p=12&n=50&sortby=name
This will give me the list of movies limited to horror movies with the default view; starting on page 12 with 50 movies per page where the movies are sorted by name.
Actions
The last thing needed are your action on the resource. The action are either collection based or item based.
/movies/horror/
GET -> Get resources as a list
POST -> Create, Update
/movies/horror/10/
GET -> Get resource as item
POST -> Update
I hope this helps.
I disagree with the edit. Queries should be defined by querystrings as per Martijn Laarman's post. i.e.:
/movies?genre=action&timeframe=90s&lbound=20&ubound=40&order=desc
Well, the way I see it some of the information you return now as objects could simply be added to the metadata of its parent object.
For instance, rating could be part of the response of /movies/5
<movie>
<title>..</title>
..
<rating url="movies/ratings/4">4</rating>
<tags>
<tag url="movies/tags/creative">creative</tag>
...
Removing a tag simply means posting the above response without that tag.
Also queries should go in URL variables, I believe:
/movies/?startsWith=Forrest%20G&orderBy=DateAdded
Based on my understanding of ROA (I'm only on chapter five of RESTful Web Services) it looks good to me.
This is an awesome initial draft for a spec of a REST API. The next step would to specify expected return codes (like you did with "404 No Tag Available"), acceptable Content-Types, and available content-types (e.g., HTML, JSON). Doing that should expose any additional chinks you'll need to hammer out.
#Nelson LaQuet:
Using the HTTP methods as they are actually defined gives you the safety of knowing that executing a GET on anything on a web site or service won't eat your data or otherwise mangle it. As an example (pointed out in RESTful Web Services) Google's Web Accelerator expects this behaviour -- as stated in the FAQ -- and presumably other services do too.
Also it gets you idempotency for free. That is doing a GET, DELETE, HEAD or PUT on a resource more than once is the same as doing it only once. Thus if your request fails then all you have to do is run it again.
This is not REST.
A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). Servers must have the freedom to control their own namespace. Instead, allow servers to instruct clients on how to construct appropriate URIs, such as is done in HTML forms and URI templates, by defining those instructions within media types and link relations. [Failure here implies that clients are assuming a resource structure due to out-of band information, such as a domain-specific standard, which is the data-oriented equivalent to RPC's functional coupling].
http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven