I'm doing some research to help me develop a REST API and this is one topic I haven't seen discussed in depth anywhere.
If I have a user in the system, is it better to identify the user using a numeric identifier
/users/1
Or using a string identifier?
/users/RSmith
I can see hypothetical potential pros and cons to each approach, string identifiers are more human readable, less discoverable (can't be incremented to find valid users), and don't require storing another numeric id in the database (I wouldn't want to expose database ids through the API). Numeric identifiers have no inherent meaning and due to that, can be guaranteed to be immutable, whereas with a string id the user might want to rename the resource, thus changing the resource URI.
Is there a REST best practice here or does the best approach vary to system to system? If the latter, are there any additional pros and cons associated with each method?
As you know, strictly speaking, there is no advantage between both approaches. Yes, string identifies may be easier for people to remember, but apart from that, REST does not enforce "pretty" URLs (or IDs), because most of the time URLs are accessed by programs following the hyperlinks.
Thus, human friendly URLs should only be used for bootstrapping resources that may be remembered by humans. Also, ID guessing should not be a problem because either:
You have to restrict access to URLs based on any authentication method, or:
You have to use randomized/unguessable URLs that are not "public".
So which one to use? Most of the time, it does not matter, as IDs are not accessed directly. If you have to ensure people remember their URLs for some reason, try to do them human-friendly, but try to avoid resource-name change and apply some other means of authentication so that even guessed URLs don't get access to unauthorized places.
Only advantage of this: /users/RSmith is that it's more human friendly. From RESTfull perspective it doesn't matter because both are valid resource identifiers. Everything else depends on your system requrements.
Related
Lets assume we have some main-resource and a related sub-resource with 1-n relation;
User of the API can:
list main-resources so GET /main-resources endpoint.
list sub-resources so GET /sub-resources endpoint.
list sub-resources of a main-resource so one or both of;
GET /main-resources/{main-id}/sub-resources
GET /sub-resouces?main={main-id}
create a sub-resource under a main-resource
POST /main-resource/{main-id}/sub-resouces: Which has the benefit of hierarchy, but in order to support this one needs to provide another set of endpoints(list, create, update, delete).
POST /sub-resouces?main={main-id}: Which has the benefit of having embedded id inside URL. A middleware can handle and inject provided values into request itself.
create a sub-resource with all parameters in body POST /sub-resources
Is providing a URI with main={main-id} query parameter embedded a good way to solve this or should I go with the route of hierarchical URI?
In a true REST environment the spelling of URIs is not of importance as long as the characters used in the URI adhere to the URI specification. While RFC 3986 states that
The path component contains data, usually organized in hierarchical form, that, along with data in the non-hierarchical query component (Section 3.4), serves to identify a resource within the scope of the URI's scheme and naming authority (if any). The path is terminated by the first question mark ("?") and number sign ("#") character, or by the end of the URI. (Source)
it does not state that a URI has to have a hierarchical structure assigned to it. A URI as a whole is a pointer to a resource and as such a combination of various URIs may give the impression of some hierarchy involved. The actual information of whether URIs have some hierarchical structure to it should though stem from link relations that are attached to URIs. These can be registered names like up, fist, last, next, prev and the like or Web linking extensions such as https://acme.org/rel/parent which acts more like a predicate in a Semantic Web relation basically stating that the URI at hand is a parent to the current resource. Don't confuse rel-URIs for real URIs though. Such rel-URIs do not necessarily need to point to an actual resource or even to a documentation. Such link relation extensions though my be defined by media-types or certain profiles.
In a perfect world the URI though is only used to send the request to the actual server. A client won't parse or try to extract some knowledge off an URI as it will use accompanying link relation names to determine whether the URI is of relevance to the task at hand or not. REST is full of such "indirection" mechanism in order to help decoupling clients from servers.
I.e. what is the difference between a URI like https://acme.org/api/users/1 and https://acme.org/api/3f067d90-8b55-4b60-befc-1ce124b4e080? Developers in the first case might be tempted to create a user object representing the data returned by the URI invoked. Over time the response format might break as stuff is renamed, removed and replaced by other stuff. This is what Fielding called typed resources which REST shouldn't have.
The second URI doesn't give you a clue on what content it returns, and you might start questioning on what benefit it brings then. While you might not be aware of what actual content the service returns for such URIs, you know at least that your client is able to process the data somehow as otherwise the service would have responded with a 406 Not Acceptable response. So, content-type negotiation ensures that your client will with high certainty receive data it is able to process. Maintaining interoperability in a domain that is likely to change over time is one of RESTs strong benefits and selling points. Depending on the capabilities of your client and the service, you might receive a tailored response-format, which is only applicable to that particular service, or receive a more general-purpose one, like HTML i.e.. Your client basically needs a mapping to translate the received representation format into something your application then can use. As mentioned, REST is probably all about introducing indirections for the purpose of decoupling clients from servers. The benefit for going this indirection however is that once you have it working it will work with responses issued not only from that server but for any other service that also supports returning that media type format. And just think a minute what options your client has when it supports a couple of general-purpose formats. It then can basically communicate and interoperate with various other services in that ecosystem without a need for you touching it. This is how browsers operate on the Web for decades now.
This is exactly why I think that this phrase of Fielding is probably one of the most important ones but also the one that is ignored and or misinterpreted by most in the domain of REST:
A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types. (Source)
So, in a true REST environment the form of the URI is unimportant as clients rely on other mechanisms to determine whether to use that URI or not. Even for so called "REST APIs" that do not really care about the true meaning of REST and treat it more like old-school RPC the question at hands is probably very opinionated and there probably isn't that one fits all solution. If your framework supports injecting stuff based on the presence of certain query parameters, use that. If you prefer the more hierarchical structure of URIs, go for those. There isn't a right or wrong in such cases.
According to the URI standard when you have a hierarchical relationship between resources, then better to add it to the path instead of the query. https://datatracker.ietf.org/doc/html/rfc3986#page-22 Sometimes it is better to describe the relation itself, not just the sub-resource, but that happens only if the sub-resource can belong to multiple main resources, which is n:m relationship.
According to Rest, we should use GET if we have to retrieve some data, and use POST if it's creating a resource.
But, if we have to take multiple parameters (more than 7-8), or list of UUIDs let's say, then shouldn't we use POST rather than GET?
To avoid:
The complexity of the URL
Future scope to incorporate any new field
URL length which may limit us in future
If we are not encoding our URL then we may risk of exposing params (least significant point though)
Thanks.
GET Vs POST in REST
Use GET when the semantics of GET fit the use case; in other words, when you are trying to retrieve a copy of the latest representation of some resource.
Use POST when no other standardized method supports the semantics you need.
You can use POST for everything, but you give up the advantages of using more specialized methods -- the ability of general purpose components to do intelligent things because they understand the semantics of the request. For instance, GET has safe semantics, which means that a general purpose component knows that it can pre-fetch a representation of the resource before the user needs it, or automatically repeat a request if it doesn't get a response from the server.
What HTTP gives you is a extendable collections of refinements of the general purpose POST method, adding more specific constraints so that general purpose components can leverage the resulting properties.
The complexity of the URL
Complexity in the URL really isn't a big deal, as far as general purpose components are concerned a URL is just a opaque sequence of bytes that happens to abide by certain production rules. For the most part, the effective target-uri of a web request is treated as an atomic unit, so what might seem "complex to a human being doesn't bother the machines at all (for instance, take a look at the URL used when you submit a search from the google home page).
URL length which may limit us in future
We care a bit about URL length. RFC 3986 doesn't restrict the length of the URI, but some implementations of general purpose components will fail if the length is far outside the norm. So you probably don't want to include a url encoded copy of the unabridged works of Shakespeare in the query part of your request.
Future scope to incorporate any new field
Again, there's not a lot of difference here. Adding new optional elements to a URI template is really no different than adding new optional elements to a message template.
we may risk of exposing params
We also want to be careful about sensitive information - as far as the machines are concerned, the URI is an identifier; there's no particular reason to worry about a specific sequence of bytes. Which means that the URI may be exposed at rest (in a clients history, or list of bookmarks, in the servers access logs). Restricting sensitive information to the body of a message reduces the chance of the data escaping beyond its intended use.
Note that REST, and leveraging the different HTTP methods, isn't the only way to get useful work done. SOAP (and more recently gRPC) decided that a different collection of trade offs was better -- in effect, reducing HTTP to transport, rather than an application in itself.
According to Rest, we should ... use POST if it's creating a resource.
This is an incorrect interpretation of REST. It's a very common interpretation, but incorrect. The semantics of POST are defined by RFC 7231; it means that, and not something else.
The suggestion that POST should only be used for create is a misleading over simplification. The earliest references I've been able to find to it is a blog post by Paul Prescod in 2002; and of course it became very popular with the arrival of Ruby on Rails.
But recall: REST is the architectural style of the world wide web. HTML, the most common hypertext media type in use on the web, has native support for only two HTTP methods; GET, used to fetch resource representations from a server, and POST which does everything else.
You should also use POST if you have sensitive data such as username and or password which are best encoded as form parameters (key value pairs)
I'm designing a REST API and I'm a big defender of keeping my URL simple, avoiding more than two nested resources.
However, I've been having second thoughts because of data security restrictions that apply to my APIs, that have been trying to force me to nest more resources. I'll try to provide examples to be more specific, as I don't know the correct naming for this situation.
Consider a simple example where I want to get a given contact restriction for a customer, like during what period my customer accepts to be bothered with a phone call:
So, I believe it's simpler to have this:
- GET /customers/12345
- GET /customers/12345/contacts
- GET /contacts/9999
- GET /contacts/9999/restrictions
- GET /restrictions/1
than this:
- GET /customers/12345
- GET /customers/12345/contacts
- GET /customers/12345/contacts/9999
- GET /customers/12345/contacts/9999/restrictions
- GET /customers/12345/contacts/9999/restrictions/1
Note: If there are more related resources, who knows where this will go...
The first case is my favourite because since all resources MUST have a unique identifier, as soon I have its unique identifier I should be able to get the resource instance directly: GET /restrictions/1
The data security restriction in place in my company states that not everyone can see every customers' info (eg: only some managers can access private equity customers). So, to guarantee that, the architects are telling me I should use /customers/12345/contacts/9999/restrictions/1 or /customers/12345/contact-restrictions/1 so that our data access validator in our platform has the customerId to check if the caller has access to it.
I understand the requirement and I see its value. However, I think that this kind of custom security informatio, because that's what I believe to be, should be in a custom header.
So, I believe I should stick to GET /restriction/1 with a custom header "customerId" with the value 12345.
This custom header would only be needed for the apis that have this requirement.
Besides the simpler URL, another advantage of the header, is that if an API didn't start with that security requirement and suddenly needs to comply to it, we could simply require the header to be passed, instead of redefining paths.
I hope I made it clear for you and I'll be looking to learn more about great API design techniques.
Thank you all that reached the end of my post :)
TL;DR: you are fighting over URI design, and REST doesn't actually offer guidance there.
REST, and REST clients, don't distinguish between your "simpler" design and the nested version. A URI is just an opaque sequence of bytes with some little domain agnostic semantics.
/4290c3b2-134e-4647-867a-214d0c866f29
Is a perfectly "RESTFUL" URI. See Stefan Tilkov, REST: I don't Think it Means What You Think it Does.
Fundamentally, REST servers are document stores. You provide a key (the URI) and the server provides the document. Or you provide a key, and the server modifies the document.
How this is implemented is completely at the discretion of the server. It could be that /4290c3b2-134e-4647-867a-214d0c866f29 is used to look up the tuple (12345, 9999, 1), and then the server checks to see if the credentials described in the request header have permission to access that information, and if so the appropriate representation of the resource corresponding to that tuple is returned.
From the client's perspective, it's all the same thing: I provide an opaque identifier in a standard way, and credentials in a standard way, and I get access to the resource or I don't.
the architects are telling me I should use /customers/12345/contacts/9999/restrictions/1 or /customers/12345/contact-restrictions/1 so that our data access validator in our platform has the customerId to check if the caller has access to it.
I understand the requirement and I see its value. However, I think that this kind of custom security information, because that's what I believe to be, should be in a custom header.
There's nothing in REST to back you up. In fact, the notion of introducing a custom header is something of a down check, because your customer header is not something that a generic component is going to know about.
When you need a new header, the "REST" way to go about it is to introduce a new standard. See RFC 5988 for an example.
Fielding, writing in 2008
Every protocol, every media type definition, every URI scheme, and every link relationship type constitutes prior knowledge that the client must know (or learn) in order to make use of that knowledge. REST doesn’t eliminate the need for a clue. What REST does is concentrate that need for prior knowledge into readily standardizable forms.
The architects have a good point - encoding into the uri the hints that make it easier/cheaper/more-reliable to use your data access validator is exactly the sort of thing that allowing the servers to control their own URI namespace is supposed to afford.
The reason that this works, in REST, is that clients don't depend on URI for semantics; instead, they rely on the definitions of the relations that are encoded into the links (or otherwise expressed by the definition of the media type itself).
AFAIU, RESTful API use http nomenclature to organize API.
From an exemple taken best practices you'll end up with:
http://api.college.com/students/3248234/courses/physics
What's the real advantage, from more naïve approach such as:
http://api.college.com/course_show?STUDENTID=3248234&COURSETYPE=physics
http://api.college.com/ratings_show?STUDENTID=3248234&COURSETYPE=physics
From my view point, first uri mixes stuffs: objects (students, courses) and their params (id for students, type for course). I fell it's a mess.
Moreover, we don't really know what's shown. It is the course taken by this student ? Or the rating our student achieved ? We can suspect that ratings would have been added at the end like .../courses/physics/ratings, but we can't be sure, since the last word is a parameters, not an object.
Second approach is more object oriented, like Course.show(**kwargs), and anyway has the advantage to separate function (or call it method) and parameters (or call it arguments).
Additional point, with this semantic, you can do CRUD but can be more detailed in your interface, like /course_delete or /course_suspend or /course_postpone
So two questions:
1- what's the real advantage of REST nomenclature on web API routing ? Isn't just hype ?
2- from security viewpoint, in my exemples, I suspect first url is less secure than second (flask had a problem with that if I remember well), is it correct ?
what's the real advantage of REST nomenclature on web API routing ? Isn't just hype ?
REST doesn't care what spelling you use for your identifiers.
URI Templates are a convenient way to generalize identifiers for different resources; this convenience is most frequently seen in mapping resource identifiers to implementations in the server, but are also sometimes seen as a means to describe a family of identifiers to a client that understands hypermedia representations.
Judicious use of path segments when describing a hierarchy of resources, allows you to take advantage of the client's ability to resolve relative references.
from security viewpoint, in my exemples, I suspect first url is less secure than second (flask had a problem with that if I remember well), is it correct ?
No? Neither of them offers any security at all; they are just identifiers.
Simple question I'm having trouble finding an answer to..
If I have a REST web service, and my design is not using url parameters, how can I specify two different keys to return the same resource by?
Example
I want (and have already implemented)
/Person/{ID}
which returns a person as expected.
Now I also want
/Person/{Name}
which returns a person by name.
Is this the correct RESTful format? Or is it something like:
/Person/Name/{Name}
You should only use one URI to refer to a single resource. Having multiple URIs will only cause confusion. In your example, confusion would arise due to two people having the same name. Which person resource are they referring to then?
That said, you can have multiple URIs refer to a single resource, but for anything other than the "true" URI you should simply redirect the client to the right place using a status code of 301 - Moved Permanently.
Personally, I would never implement a multi-ID scheme or redirection to support it. Pick a single identification scheme and stick with it. The users of your API will thank you.
What you really need to build is a query API, so focus on how you would implement something like a /personFinder resource which could take a name as a parameter and return potentially multiple matching /person/{ID} URIs in the response.
I guess technically you could have both URI's point to the same resource (perhaps with one of them as the canonical resource) but I think you wouldn't want to do this from an implementation perspective. What if there is an overlap between IDs and names?
It sure does seem like a good place to use query parameters, but if you insist on not doing so, perhaps you could do
person/{ID}
and
personByName/{Name}
I generally agree with this answer that for clarity and consistency it'd be best to avoid multiple ids pointing to the same entity.
Sometimes however, such a situation arises naturally. An example I work with is Polish companies, which can be identified by their tax id ('NIP' number) or by their national business registry id ('KRS' number).
In such case, I think one should first add the secondary id as a criterion to the search endpoint. Thus users will be able to "translate" between secondary id and primary id.
However, if users still keep insisting on being able to retrieve an entity directly by the secondary id (as we experienced), one other possibility is to provide a "secret" URL, not described in the documentation, performing such an operation. This can be given to users who made the effort to ask for it, and the potential ambiguity and confusion is then on them, if they decide to use it, not on everyone reading the documentation.
In terms of ambiguity and confusion for the API maintainer, I think this can be kept reasonably minimal with a helper function to immediately detect and translate the secondary id to primary id at the beginning of each relevant API endpoint.
It obviously matters much less than normal what scheme is chosen for the secret URL.