I'm sure this topic must have been covered off before so I'm happy to be pointed to any articles etc that I may have missed while searching.
I need to implement a very simple REST API to add and retrieve records in a master/detail relationship. My two options are below:
OPTION 1
POST /master
POST /master/[id]/details
GET /master/[id]
GET /master/[id]/details
PROS
Feels more 'RESTful'
Can retrieve fine-grained data for performance
CONS
A master doesn't make sense without at least one detail. How to handle atomicity? Compensating DELETE on the master if the detail fails when adding?
Multiple calls required to retrieve the master/detail set
OPTION 2
POST /master_and_details
GET /master_and_details/[master id]
PROS
Easy to manage atomicity
CONS
More complex structure to manage
GETs must return entire structure (not always efficient)
Doesn't feel very 'RESTful'
Thanks,
John
REST more or less dictates option 1, option 2 is just a plain old http api.
Your statement that a master makes no sense without at least one detail is probably wrong. You have no idea how your api will be used in the future by clever developers. You can guess, but you don't really know.
If you really need the compound solution yourself you could always add an interface at a higher level that calls onto the two separate interfaces and returns a compound object.
Option 1 allows the possibility of a microservice implementation -- or at least, a separation of concerns into two separable objects. Option 2 alone would not.
At least for sake of argument, and risking my meager reputation, I'll venture a third approach.
You'd have two resources: master and detail. (not "details", unless you want to go totally plural, in which case you'd have "masters" and "details"). The representation of master would include the collection of details, or (better) link relations to the details. Consider https://www.rfc-editor.org/rfc/rfc6573 for that link relation.
Therefore GET of master includes (directly or indirectly via relation) the collection of detail. POST/PUT of master can also POST/PUT detail members by their presence in the form data.
Any detail can also be GET/PUT/POSTED independently of the master. Here, the design depends somewhat on whether a detail has its own primary key, vs. using a composite key with its master. If it has its own primary key then you could have:
GET /detail/{detailKey} - gets specific detail
POST /detail - creates new detail
GET /master/{masterkey}/detail -- gets all details for master
GET /master{masterkey}/detail/{detailkey} -- get specific detail
obviously, on the POST, the data must include the key of the master.
You see that there is more than one URI for getting a detail. I don't think that that's wrong, but it does introduce some ambiguity: if detailKey is not actually a child of masterKey, do you 404? I'd say yes.
if the detail uses a composite key, then it can't be GETed independently of the master, so the first form shown above could not be supported. Also, {detailKey}, as used in the last example above, would be the identifier of the detail item within the master (typically a sequence number).
I would definitely go with Option 1, as Option 2 has nothing to do with REST. But that does not mean, that you need to use multiple querys to get Master + Detail.
As the Detail belongs to the Master (and so it is part of it), in my opinion it is absolutely ok to return Master and the Detail, when querying the Master.
You might also consider using a parameters to control whether to send the Detail or not.
So instead of executing GET /master/1 you could use GET /master/1?detail=true.
For POST and PUT more or less the same would work. Just instead of having the query parameter, you have a "section" in your body regarding the Detail.
An Example using JSON:
{
"data": {
"name": "master",
"detail": [
{
"name": "detail1"
},
{
"name": "detail2"
}
]
}
}
A POST with this data could create a Master with the name "master" and 2 Details for this Master.
As #ElroyFlynn stated before, the Details could also be accessible without the Master, if this makes sence.
Think about a forum with Threads and Posts. Usualy a Thread is the Master and every Post is a Detail. But if you want to search all Posts of the last hour, you definitely want to query the posts directly (the query could be something like GET /post?max_age=1h).
Also I don't agree, that a Master without Detail does not make sence. There might be cases, where it doesn't, but in the case of the forum, a Thread on it's own makes sence.
For the atomicity:
It depends on the case: If you delete a User you usually keep his Posts (even here on StackOverflow). If you instead delete a Thread, I guess, you can delete the Posts to.
Related
I'm having a problem to solve in an application. I'll show an example about it.
I have a rabbitmq queue on a system that is responsable to return Orders, called by another systems (the communication among these systems is only throught message). Until then, the only possible Order search was by the order code.
It works well. When I search by order code, I also filter by the order with contracts and deleted (logically). So, if the order has no contracts or it was deleted, the query doesn't return registers.
Now, one of that systems needs to find Orders without contracts and/or deleted.
Basically, I believe I need to build the same logic used in an API rest like this one, but using a queue message:
/api/orders?id=123455?deleted=true&hasContracts=true
Do that it's easy with message. I just need send a message with this format.
{
"code": 123,
"deleted": true,
"hasContract": true
}
Mapping the values for Long and Boolean classes. If the information was null, this filter will be ignored by the query, except the code that's mandatory.
The doubt is: is this makes sense? I didn't find anything about this subject on the Internet. Create a queue for each case is not an option, because it will be hard for us to implement many queues.
It makes sense to me; using RPC with RabbitMQ is like RPC with HTTP/gRPC/..., so you have many options here:
if you need a great flexibility, you can
create your own query language (like in the example above)
use something like GraphQL
if your use cases are limited, you can choose to segregate the API endpoints with several routing keys (REST-over-AMQP).
Hope this helps.
I want to implement a REST endpoint to figure out whether a particular item can be closed. How can I do it properly from the point of view of REST design?
/items/canbeclosed - sounds ugly
maybe something like
/items/status but I have already status endpoint used for another purpose
How can I do it properly from the point of view of REST design?
REST doesn't care what spelling you use for your URI.
It might help to think about what's true of the items now, rather than thinking about which actions are possible. For instance, if the items "can be closed" because they are "completed", then /items?completed or /items/completed or even '/completedItems' might be reasonable spellings.
Alternatively, perhaps these are tasks, sitting in a task queue, with different queues set up for your different stages. So something like /stages/closeItem/tasks might be the right thing.
Your touchstone, as with all things REST, is to imagine your API as a web site. If you had to navigate to a web page to access this information, what would the identifier of the web page be? and there you go.
You have several different possibilities depending on what you want to do specifically.
For getting whether a particular item can be closed, retrieve the item and have a field that answers this question:
GET /items/{itemNumber}
{
"canBeClosed": true,
...
}
You could also use a subresource for that particular field:
GET /items/{itemNumber}/can-be-closed
true
This would also be "resty" but it's only helpful in some edge cases. You wouldn't want to make a call over the network for every little piece of information. This would result in bad performance.
Note that the solutions above point to a single resource identified by its ID. Have a look at restapitutorial.com for the difference between collection resources and item resources.
Maybe you want to get a list of the items which are closable:
GET /items?closable=true
In this case I'd like using a matrix param more:
GET /items;closable=true
Matrix params are much like query params except that they are bound to a particular path element rather than to the end result. So you can have something like this:
GET /customers;country=Germany/orders
which would return the orders of customers living in Germany. This is not possible with query params as they are always added to the end. So in my eyes they are better for paging and sorting while matrix params are better for filtering.
I have read many of the other posts on designing many to many relationships restfully; however, i couldn't quite found the answers i was looking for.
Basically, I have 2 tables in the database and another many-to-many table that bridges these 2 tables.
For simplicity lets call them:
Course(CourseID, CourseName)
Instructor(InstructorID, InstructorName)
CourseInstructor(CourseID, InstructorID, lectureRoomName) (MANY-TO-MANY)
I have 2 questions:
1) How should I get all the entries inside the CourseInstructor table.
Is GET /courses/instructors correct? (or GET /instructors/courses or both)
Or should I have a seperate endpoint called /coursesinstructors
2) Also I want to be able to get all rows in the CourseInstructor table by passing in CourseName.
Normally i think i would do, /courses?name=coursename if i was searching for a course. And, I might use the endpoint /courses/{courseId}/instructors/{instructorId} to search for a specific entry in the many-to-many table.
Thus, I am curious would something like this work: /courses?name=coursename/instructors (Is this even possible?)
Another alternative is to have another endpoint called /coursesinstructors and i can make /courseinstructor?name=coursename to get the results.
Or should i make 2 calls to get the results by doing:
- get the id through /courses?name=coursename
- and follow it with courses/{id}/instructors
I am open to any other suggestions and solutions other than the ones I came up with above (not sure mine are correct solutions anyways).
For the first scenario, it really depends on what your use cases are. GET /courses/instructors would imply (only) retrieving the instructors, so I'd personally go with just GET /courses, and embed the instructors.
As for the second scenario, /courses?name=coursename should be good enough (plus /courses/{courseId}/instructors/{instructorId} for drilling down to a specific entry). The 2 calls is another valid option. However, an URL like /courses?name=coursename/instructors doesn't seem to be valid, according to RFC 3986.
If you really do need (1), a full dump of all course-instructor pairings, then I think your best bet is to support
GET /course-instructors
GET /course-instructors?courseName=whatever
You can then either link to or embed the course and the instructor.
{
"course": "/courses/12",
"instructor": "/instructors/54"
}
{
"course": {
"name": "Political Science",
...
},
"instructor": {
"name": "Hober Mallow",
...
}
}
What you return depends on the needs of your clients. Perhaps you return abbreviated information plus a link to the full representation, or perhaps it's ok to return just the links because courses and instructors are (should be!) highly cacheable.
As an aside, I think you'd be well-served to stop thinking about how the database stores data, and rather think about what clients need from your API.
So I'm building a REST api and need to make some urls. The problem is, I'm running into some conflicting paths. For example:
GET <type>/<id> gets the details of an object of a given type and id
GET <type>/summary gets the summary of objects of a given type
This simplified example shows a problem occurs when an object has id "summary". What is the best way to solve this? From a REST puritan perspective, what should be the solution?
Here's some of my ideas:
Put the <id> in query parameters. From what I understand this is against standards
Put a keyword at the start of the url. Also against standards?
Disallow certain id values. Not something I want to enforce for all my users and use cases and different entrances into my system
I may have an alternative to this. What if we have both book as wel as the plural books. Then you can have:
/book/{id}
and
/books/summary
or
/books/count
The URL structure is not quite right to begin with so it's difficult to solve it in a clean way.
For the sake of discussion, let's assume <type> is a books resource. So the first URL is fine - you get a book of the given ID:
GET /books/<id>
However this is not:
GET /books/summary
Because it's a bespoke URL, which I guess has a use in your application but is not restful. A GET call should return one or more resources. However a "summary" is not a resource, it's a property of a resource and that's why you end up in this situation of having IDs mixed up with book properties.
So your best option would be to change this URL to something like this:
GET /books?fields=summary
By default GET /books would return all the resources, while GET /books?fields=<list_of_fields> will return the books but with only the chosen properties.
That will be similar to your previous URL but without the ID/property conflict, and will also allow you later on to retrieve resources with specific fields (without having to create new custom URLs).
Edit:
Regarding the count of books, it's still useful to reason in terms of resources. /books gives you one or more books, but it should not be used for meta-information about the collection, such as count, but also things like "most read book", or "books that start with the letter 'A'", etc. as that will make the resource more and more complex and difficult to maintain.
Depending on what you want to achieve I think there'd be two solutions:
Create a new resource that manages the collection of books. For example:
GET /bookcase
And that will give you information about the collection, for example:
{
"count": 1234,
"most_read": "<isbn>",
// etc. - any information that might be needed about the book collection
}
Or a search engine. You create a resources such as:
GET /book_search_engine/?query=
which would return a search result such as:
{
"count": 123,
"books": [
// Books that match the query
]
}
then a query like this would give you just the count:
// Search all the books, but provide only the "count" field
GET /book_search/?query=*&fields=count
Obviously that's a more involved solution and maybe not necessary for a simple REST API, however it can be useful as it makes it easier to create queries specific to a client.
This simplified example shows a problem occurs when an object has id "summary". What is the best way to solve this? From a REST puritan perspective, what should be the solution?
As far as REST is concerned, the URI are opaque. Spelling is absolutely irrelevant. You could use URI like
/a575cc90-2878-41fe-9eec-f420a509e1f0
/f871fff6-4c4e-48f7-83a4-26858fdb3096
and as far as REST is concerned, that's spot on. See Stefan Tilkov's talk REST: I Don't Think It Means What You Think It Does.
What you are asking about is URI design, how to adapt conventions/best practices to your particular setting.
One thing that will help is to recognize is that summary is a resource, in the REST/HTTP sense -- it is a document that can be represented as a byte sequence. All you need to do is figure out where that resource belongs (according to your local spelling conventions).
Continuing to borrow the "books" example used by others
# Here's the familiar "URI that identifies a member of the books collection"
/books/<id>
# Here's the summary of the /books collection
/summaries/books
Put the in query parameters. From what I understand this is against standards
Not as much as you might think. REST doesn't care. The URI spec expresses some views about hierarchical vs non hierarchical data. HTTP supports the notion of a redirect, where one resource can reference another.
GET /books?id=12345
302 Found
Location: /books/12345
You also have options for skipping a round trip, by returning the representation you want immediately, taking advantage of Content-Location
GET /books?summary
200 OK
Content-Location: /summaries/books
...
I have the same issue. And all the solutions seem a little off b/c REST best practices seem to suggest none of them are ideal.
You could have just one off-limit id, like all.
GET <type>/<id>
GET <type>/all/summary
It might even be possible to use a single symbol instead, such as ~ or _.
GET <type>/<id>
GET <type>/~/summary
How satisfying this solution seems is of course very subjective.
The singular/plural approach seems more elegant to me but despite most REST best practice guides saying not to do this. Unfortunately some words don't have distinct singular and plural forms.
This isn't perfectly conventional for how some like to define their rest endpoints.
But I would would enforce a pattern where "id" cannot be any string. Instead I would use a uuid and define my routes as such.
GET /books/{id:uuid}
GET /books/{id:uuid}/summary
And if you really want a verb in the URL without an identifier it is still technically possible because we know the {id:uuid} in the path must conform to the uuid pattern.
With that GET /books/summary is still distinct from GET /books/{id:uuid}
I asked a question a while back i.e. "How save an entire backbone collection?". However what intrigues me is that why is a save method not offered? Is it unRESTful to save (PUT/POST) entire collections or is it uncommon to do so in the REST-land?
GET: /MySite/Collections - allowed by collection.fetch()
POST: /MySite/Collections - for the model(s) in the collection to be Posted when calling model.save()
PUT: /MySite/Collections/{id} - for the model(s) to be updated individually
GET: /MySite/Collections/{id} - to fetch an individual model throuth model.fetch()
So why not allow for POST/PUT an entire collection of resources? It is convenient sometimes and although one can wrap/hack out some code using collection.toJSON why not include it? I'm just curious about its absence and the rationale for the same. Frameworks not having the capability of a few things usually implies bad programming/design and are thus left out. Is saving an entire collection 'bad practice'?
The wikipedia article about REST does mention CRUD verbs for collection.
But, in my opinion, a Collection is not a resource, it is not an entity, and it has not state. It is, instead, a bunch of resources. And if there would be an UPDATE command for a Collection it would be nothing else but a multiple UPDATE commands over multiple Models. Having the possibility of multiple UPDATE commands in only one request would be helpful but I think this is not a job for the REST implementation.
Also there will be problems of ambiguity, for example in a Collection that contains already saved Models with id and so on, and others that not, what will a POST command mean?... or an UPDATE command?...
No talking about the increase of the complexity in the server side where, if this Collection REST support should be taken like standard, we should to work the double to accomplish the casuistic.
Summarizing: I don't see any case where the need of a Collection REST command can't be solved with the actual, simpler, only-Model REST commands, so keeping the things as simple as possible I think is a good habit.