I'm completely new to REST. I helped to implement something that was called REST at work but it breaks so many of the rules that it's hard to qualify it as REST. I want to follow the HATEOAS guideline and the remaining question I have is regarding documentation of media types and their specification. Namely when one media type is really an extension of another.
For example, I've decided on 'application/hal+json' for the base media type. Everything that a user would receive is going to be a HAL blob with some added fields. I don't want to call my media types just 'application/hal+json', it seems to me more information should be available than that, but I want it to be clear that this is what they are in addition to the extra fields that are my data. Furthermore my system is going to end up having some of these fields inherited and such in both the request (which won't be HAL blobs) and response formats. A generic "User" type might have just a user id and name for example while an extension like "Student" or "Teacher" will have different additional fields.
Does it make sense to represent this extension somewhere in the media type itself? Do people generally document the relationship in their HATEOAS documentation links? If so, what's the general trend here? I want my API to be easy to use and thus figure it should follow norms that are available.
Couple of things i'd like to point out about your move to a true RESTful architecture.
First, RESTful APIs must perform content negotiation. To say your base type is hal+json is seems strange. It sounds like you want to have types like parent+hal+json or maybe hal+json;type=parent. This would mean your client would have to understand these types specifically...and this would be not very RESTful because it's just a local implementation. This is fine in the real world..you can do this...almost everyone does stuff like this.
To be true RESTful api you'd have to offer similar support for other content types...that could get messy.
Now specifically to HAL, there's two things available to you so that your client can "discover" what data types they are getting back. One is CURIEs and the other is Profiles https://datatracker.ietf.org/doc/html/draft-kelly-json-hal-06#section-5.6 I think Profiles is more what you're after here as it allows you to document conventions and constraints for the resource that will be retrieved.
But don't count out CURIE's either. There's lots of defined semantics out there already. Your model might fit one of http://schema.org sets and then you can just use their link relationships and the client should know what's going on.
If you really want a lot of control on the semantics of your resources...you may want to look at http://json-ld.org/ with it's #context concept would be a good reference.
In my opinion this is an area where the examples are very thin, especially for HAL. I've yet to seen a client smart enough to parse and care about semantics at run time. What i think is important is when someone is building the client that info is available they can figure out that a Student is a Person. One day the thing building the clients will be client generator code and it'll use that info to build a nice client side object model for you.
TL;DR if you stick with HAL use CURIEs and Profiles to get what you want.
This question is an extremely open ended discussion and it really depends on how different engineers interpret REST standards and best practices. Nonetheless, As a fellow software engineer with enough experience with REST services development (and faced the same questions professionally as you did), I would add my inputs here.
REST service development rules are heavily dependent on the url definitions. It's very important that you expose your apis in such a way that your clients can understand exactly what's happening per api just by looking at the url definition.
With that being said, different clients (and for that matter different engineers) view the best practices differently. For example, if you are trying to search a User by email, there are atleast two approaches
1) GET /users/emails/{email} // A client can interpret this as
"getting a user by email"
2) GET /users?email={email} // A client can interpret this as
"searching a user by email" because of query param
3) GET /users/email={email} // This can be interpreted just as # 1
It depends on the developer how they want to expose this api and how they document it for the clients. All the approaches are correct in different perspectives.
Now going specific to your questions. Here's what my approach would look like in terms of "User", "Student" and "Teacher".
I look at each of these 3 as separate resources? Why? because they are separate types even though 2 of them are extended from the 3rd one. Now how would my apis look like for these?
For Student:
1) Retrieving a list of students : GET /students
2) Retrieving a Student with id: GET /students/{id}
3) Creating a Student : POST /students
4) Updating a student : PUT /students/{id}
5) Delete a student: DELETE /students/{id}
6) Search a student: GET
/students?{whateverQueryParamsYouWantForSearch}
The same will be applied for Teacher as well.
Now here's for User.
1) GET /users : Retrieving list of all the users (Students and
Teachers)
2) GET /users?type={type} : Here's the kicker. You can specify the
type to be student OR teacher and you will return the data of specific
type (properly documented ofcourse)
3) POST /users?type={type} : Create a specific TYPE of user
(student or teacher)
.. And so on.
The main difference is .. the apis with root url /users can be used for BOTH types of users (Provided the type is always specified and documented to the clients). While the Student and Teacher apis are specific to those types.
My money has always been on specific types while generic types for searching (meaning for searching both types of users .. use /users?params). It's the most easy way for the clients to understand what's going on. Even documenting them is much easier.
Finally talking about HATEOAS. Yes, it's a part of the standards and the best practice is to ALWAYS provide a url/link to the resource you are returning or if your return object is complex and contains other resources which might contain resources who might themselves be exposed through apis. For example,
/users?type=student&email=abc#abc.com
will return all users with that email and it's better to follow HATEOAS here and provide a url to each returned user such that the url looks like : /students/{id}. This is how we have normally handled HATEOAS
That is all I have to add. As I said earlier, its a very open ended discussion. Each engineer interprets the standards differently and there is no ONE WAY to handle all usecases. There are some base rules and the clients and other developers will applaud you if you follow them :)
Related
Suppose I have two models, Order and Customer. I've implemented an API that lets you filter on both:
/orders?status=fulfilled
/customers?city=Atlanta
But what if I want to search for orders in a fulfilled status, whose associated customer also lives in Atlanta? Is it RESTful to do something like /orders?status=fulfilled&customer.city=Atlanta? Or is there a canonical format for doing this sort of thing?
Is it RESTful to do something like /orders?status=fulfilled&customer.city=Atlanta?
Yes.
is there a canonical format for doing this sort of thing?
No.
You will normally want to choose a resource identifier that is compatible with a standardized URI Template because that lets you leverage pre-existing libraries.
But there are a lot of different ways you can expand variables into a template.
A query part composed of application/x-www-form-urlencoded key value pairs is a common choice; it gives you compatibility with HTML forms "for free". But if you aren't expecting clients to interact with your API via web pages, maybe that's not too important.
URI spellings are a lot like spellings of variable names; the machines don't care. You, therefore, have extra degrees of freedom that you can use to make life easier for some people: your clients looking at identifiers in their browsing history, your operations people looking in logs, your technical writers trying to document the API....
You'll probably want to choose something that is convenient for yourself -- so make sure that your design fits well with your routing framework.
Choosing identifier spellings that direct these requests to the same controllers that you are already using vs. a new controller -- that's entirely up to you. Part of the point of the API is that the implementation details are hidden from the client and can be changed without breaking the interface.
I created a few Rest apis right now and I always preferred a solution, where I created an endpoint for each resource.
For example:
GET .../employees/{id}/account
GET .../supervisors/{id}/account
and the same with the other http methods like put, post and delete. This blows up my api pretty much. My rest apis in general preferred redundancy to reduce complexity but in this cases it always feels a bit cumbersome. So I create another approach where I work with inheritance to keep the "dry" principle.
In this case there is a base class User and via inheritance my employee and supervisor model extends from it. Now I only need one endpoint like
GET .../accounts/{id}
and the server decides which object is returned. Also while this thins out my api, it increases complexity and in my api documentation ( where I use spring rest docs ) I have to document two different Objects for the same endpoint.
Now I am not sure about what is the right way to do it ( or at least the better way ). When I think about Rest, I think in resources. So my employees are a seperate resource as well as my supervisors.
Because I always followed this approach, I tink I might be mentally run in it and maybe lost the objectivity.
It would be great if you can give my any objective advice on how this should be handled.
I built an online service that deals with this too. It's called Wirespec:
https://wirespec.dev
The backend automatically creates the url for users and their endpoints dynamically with very little code. The code for handling the frontend is written in Kotlin while the backend for generating APIs for users is written in Node.js. In both cases, the amount of code is very negligible and self-maintaining, meaning that if the user changes the name of their API, the endpoint automatically updates with the name. Here are some examples:
API: https://wirespec.dev/Wirespec/projects/apis/Stackoverflow/apis/getUserDetails
Endpoint: https://api.wirespec.dev/wirespec/stackoverflow/getuserdetails?id=100
So to answer your question, it really doesn't matter where you place the username in the url.
Try signing in to Wirespec with your Github account and you'll see where your Github username appears in the url.
There is, unfortunately, no wright or wrong answer to this one and it soley depends on how you want to design things.
With that being said, you need to distinguish between client and server. A client shouldn't know the nifty details of your API. It is just an arbitrary consumer of your API that is fed all the information it needs in order to make informed choices. I.e. if you want the client to send some data to the server that follows a certain structure, the best advice is to use from-like representations, such as HAL forms, Ion or even HTML. Forms not only teach a client about the respective properties a resource supports but also about the HTTP operation to use, the target URI to send the request to as well as the representation format to send the data in, which in case of HTML is application/x-www-form-urlencoded most of the time.
In regards to receiving data from the server, a client shouldn't attempt to extract knowledge from URIs directly, as they may change over time and thus break clients that rely on such a methodology, but rely on link relation names. Per URI there might be multiple link relation names attached to that URI. A client not knowing the meaning of one should simply ignore it. Here, either one of the standardized link relation names should be used or an extension mechanism as defined by Web linking. While an arbitrary client might not make sense from this "arbitrary string" out of the box, the link relation name may be considered the predicate in a tripple often used in ontologies where the link relation name "connects" the current resource with the one the link relation was annotated for. For a set of URIs and link relation names you might therefore "learn" a semantic graph over all the resources and how they are connected to each other. I.e. you might annotate an URI pointing to a form resource with prefetch to hint a client that it may load the content of the referenced URI if it is IDLE as the likelihood is high that the client will be interested to load that resource next anyway. The same URI might also be annotated with edit-form to hint a client that the resource will provide an edit form to send some data to the server. It might also contain a Web linking extension such as https://acme.org/ref/orderForm that allows clients, that support such a custom extension, to react to such a resource accordingly.
In your accounts example, it is totally fine to return different data for different resources of the same URI-path. I.e. resource A pointing to an employee account might only contain properties name, age, position, salery while resource B pointing to a supervisor could also contain a list of subordinates or the like. To a generic HTTP client these are two totally different resources even though they used a URI structure like /accounts/{id}. Resources in a REST architecture are untyped, meaning they don't have a type ouf of the box per se. Think of some arbitrary Web page you access through your browser. Your browser is not aware of whether the Web page it renders contains details about a specific car or about the most recent local news. HTML is designed to express a multitude of different data in the same way. Different media types though may provide more concrete hints about the data exchanged. I.e. text/vcard, applciation/vcard+xml or application/vcard+json all may respresent data describing an entity (i.e. human person, jusistic entity, animal, ...) while application/mathml+xml might be used to express certain mathematical formulas and so on. The more general a media type is, the more wiedspread usage it may find. With more narrow media types however you can provide more specific support. With content type negotiation you also have a tool at your hand where a client can express its capabilities to servers and if the server/API is smart enough it can respond with a representation the client is able to handle.
This in essence is all what REST is and if followed correctly allow the decoupling of clients from specific servers. While this might sound confusing and burdensome to implement at first, these techniques are intended if you strive for a long-lasting environment that still is able to operate in decateds to come. Evolution is inherently integrated into this phiolosophy and supported by the decoupled design. If you don't need all of that, REST might not be the thing you want to do actually. Buf if you still want something like REST, you for sure should design the interactions between client and server as if you'd intereact with a typical Web server. After all, REST is just a generalization of the concepts used on the Web quite successfully for the past two decades.
In my API I should return a list of products. In the client (browser) the user can add this products to a cart, increasing, decreasing, or remove this a product from the cart.
In my response with the products list should I put the URLs actions like this:
{
"alias": 'aliasValue',
"removeUrl": 'domain/product/alias/',
"increaseUrl": 'domain/product/alias/increase/',
"decreaseUrl": 'domain/product/alias/decrease/',
...
}
Is this a good practice, I've searched for this but the only thing that I found about URL and API is about the URL structures.
What do you think?
I would advise against reinventing the wheel. There are many ways to use HATEOAS in your API.
Consider, for example, the HAL (Hypertext Application Language) approach, where you have a _links property:
{
"_links": {
"self": {
"href": "http://example.com/api/books/1"
}
},
"id": "1",
"name": "Hypermedia as First-Class Citizen"
}
What you are trying to achieve as a name HATEOAS that stands for Hypermedia as the Engine of Application State.
So if you search for it you will find a lot of formats:
JSON-LD
HAL
SIREN
ION
JSON API
Spring
Ripozo
Source: https://nordicapis.com/tools-to-make-hateoas-compliance-easier/
As REST is just a generatlization of the browsable Web we all know, you can basically apply the same concepts as if you'd implement your system for the browser. In short, a server should provide a client with all the necessary information the client will need to make educated decisions which resources to invoke next.
In a browser-based Web application the interaction model could be similar to invoking the start page, seeing a section in the response that is of interest to you, click a link to learn further details. The next page might list some items where you are provided with further controls to modify this list, i.e. add a new item, remove one and so forth. Upon clicking some "add" button a form is presented to you which teaches you what fields a request should contain and also where this request should be sent to, even if it might not be visible to you. In the back the browser will take care of that for you. The jumping point here is, the server is actually feeding the client with any information needed to proceed through his task, whether it is the choice of available links to explore or the presentation of some guidelines on how a request should look like.
In order to determine whether a link is of interest to a client a server will use meaningful link relation names "attached" to URIs a client should use instead of parsing and interpreting URIs. This allows a server to change URIs anytime without affecting a client. Fielding mentioned the following in one of his blogposts:
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)
In addtion to that REST APIs shouldn't have typed resources meaningful to clients as this basically couples the client tightly to the API itself and will lead to failures if the serve ever changes anything in regards to the response returned. Instead content-type negotiation should be used in order to let client and server negotiate about the actual representation format used.
A media type defines the processing rules of some payload that is received for a certain Content-Type HTTP header. These rules define both syntax as well as semantics of the document. Servers in a REST architecture are i.e. allowed to reject any messages sent for a certain content type if the rules outlined in the media type are violated.
While the focus on media-types doesn't prevent changes done to the representations received completly, it still is useful in helping to decouple clients from servers further and reduce out-of-band information needed in order to interact with the service. Instead of coupling a client directly to the API, both couple to the media-type actually, and there might be multiple media types they actually couple to. If there ever needs to be a change done to a media-type you can easily introduce a new media type or, similar to HTML, specify within the media-type how backwards compatibility is achived.
In regards to prior, out-of-band information requirements, which REST doesn't completly remove, Fielding stated
Of course the client has prior knowledge. 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. That is the essential distinction between data-oriented and control-oriented integration. (Source)
As REST isn't a focus on one single aspect but a conglomeration of all of the aspects mentioned above, your actual question should be tackled from multiple angles.
First, either use existing link relations, i.e. administered by IANA or other microformats, define new ones which you should register with IANA at best or use some semantic web related tags like schema.org. I.e. if you have collections then next, prev, first and last are pretty meaningful (and already registered at IANA) for pagination or item for a decicated item within the list. This collection might have been returned previously as collection before or specified by the respective item in order to go back to the previous collection. If somethin like an add or edit should be done a link-relation like edit-form can teach the client that the URI will return some kind of form that will tell the client how a request to the API will need to look like.
Next, as basic JSON isn't that great in terms of providing help to a client, as all it does is define the syntactical structure of a document but lack support of given the elements meaning, some more advanced media types should be supported. As already mentioned by Cassio and Exadra37 there are a couple of JSON based document types that provide support for HATEOAS (~ URIs and link relation names). Instead of only going for i.e. application/hal+json a multitude of document types would be preferable as this just increases interoperablility with a magnitude of different clients that might come with support for other media types. Note futher that there is nothing wrong with returning a HTML representation as well. REST isn't restricting you to only specific JSON or XML content. I guess most of the time, instead of specifying an own media-type, simply using HTML would be sufficient to carry the meaning of the content from API to server.
You are of course free to create your own media type and use it. In order to increase interoperability you should however standardize it and probably also provide plugins/libraries for third party frameworks so that they are able to use your format as well.
The best advice, though, for sure is to reuse existing standards as the likelihood of other clients already being familiar with such implementations or concepts is way higher than for your custom format. Besides that, it probably also spares you from a lot of work and effort.
Most REST tutorials arranged resources as following:
GET /car/ -> list of cars
GET /car/<id>/ -> info about specific car
POST /car/ -> create a new car
but when building web applications for use in browsers, there is a missing link that are rarely discussed, before you can POST to /car/, you need to GET a form for creating a new resource (car). What should the URL for this form be?
I typically used:
GET /car/new/ -> form for creating a new car
POST /car/new/ -> redirect to /car/<id>/ if item is created else show form with invalid fields highlighted
but according to http://www.slideshare.net/Wombert/phpjp-urls-rest this is not a good REST URL. I can see why it's not a good REST, because "new" is really used as a verb and not a resource, but where should the form be then, because GET /car/ is already used for listing cars, so you can't use GET /car/ for the form for new cars.
In short, my question is: "What is the RESTful URL for 'create resource form'?"
On a slightly related note, even in a web service, it is sometimes not always wise to rely on the client knowing the schema in advance, therefore even in web services there could be a need to be a way for client to request the resource's current schema. AFAICS, this as a similar situation with the need to GET a create form (i.e. the form is sort of like a schema which describes how to construct POST query for creating the resource). Is my line of thought here correct?
REST does not care too much about what your URI looks like as long as it identifies one unique resource and is self-describing. Meet those criteria, and beyond that, it is personal preference. Nothing prohibits using a verb in a URI if it makes sense to use one.
In regards to your slightly related note, what you hint at with the form being a schema is media type. RESTful architecture concerns the client and the server both understanding the media types used to represent the application state.
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. Any
effort spent describing what methods to use on what URIs of interest
should be entirely defined within the scope of the processing rules
for a media type (and, in most cases, already defined by existing
media types).
Read more here: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
That is from Roy Fielding, the man who defined REST. In general, your media types should be extensible -- that is, any changes should add on and not break older clients unless necessary.
I always assumed that "form" as such is not a resource, so /<name>/new is okay - forms are not usual elements of APIs. Author of the slides put that on a "bad" list, but didn't provide a correct one - I assume he was so RESTful that he forgot to think about such cases.
I've been convinced by a fellow developer (now left) that the proper way to evolve RESTful web services is by creating custom media types for your services.
For example application/vnd.acme.payroll.v1+json
This way, you can tell your client to specify the encoding to use without changing the URI.
Is this technique a good one? Usually services embed the version into the url:
eg /acme/1.0/payroll/
I've had a lot of difficulty enforcing clients to use this scheme, especially as it seems DELETE does not enforce a media type
There are a few main signaling mechanisms you can use in a RESTful service:
The media type
The rel of a resource you are linking to.
Custom headers, like Accept-Version/Api-Version.
Each of these has distinct uses, and I will outline the ways in which we have come to understand them while designing our API.
Media Types
To signal what operations are possible on a given resource, and what the semantics of these operations are, many use custom media types. In my opinion, this is not quite correct, and a rel is more accurate.
A custom media type should tell you about the type of the data, e.g. its format or the way certain information is embodied or embedded. Having a custom media type means consumers of your API are tightly coupled to that specific representation. Whereas, using something more generic like application/json says "this is just JSON data."
Usually JSON alone is not enough for a RESTful service, since it has no built-in linking or resource-embedding functionality. That is where something like HAL (application/hal+json) comes in. It is a specialization of JSON that is still a generic format, and not application-specific. But it gives just enough to overlay the linking and embedding semantics on top of JSON that is necessary for coherently expressing a RESTful API.
Link Relation Types (rels)
This brings us to rels. To me, a custom rel is a perfect way to signal what type of resource is being dealt with or linked to. For example, a custom rel for a user resource might be http://rel.myapi.com/user, which serves two purposes:
Clients of your API must know this key ahead of time, as it is API-specific knowledge. For example, if it was available on your initial resource and you were using HAL to link to the user resource, clients might find the user link via initialResource._links["http://rel.myapi.com/user"].href.
Developers writing API clients can visit that URI in their web browser, and get an explanation of what that resource represents in your API, including what methods are applicable and what they do. This is a very convenient way to communicate that API-specific knowledge I mentioned. For examples of this, see http://rel.nkstdy.co.
If you combine rels with a standard or semi-standard media type like application/hal+json, you get resources which follow a uniform format specified by their media type, with API-specific semantics defined by their rels. This gets you almost all the way there.
Custom Headers
The remaining question is versioning. How do you allow clients to negotiate different versions of the resource, while not invalidating old URIs?
Our solution, inspired by the Restify Node.js framework, is two custom headers: Accept-Version from the client, which much match X-Api-Version from the server (or Api-Version in the upcoming Restify 2.0 release, as per the new RFC 6648). If they don't match, a 400 Bad Request is the result.
I admit that custom media types are a fairly popular solution here. In my opinion they don't fit very well conceptually, in light of the above considerations, but you would not be doing something weird if you chose them as your versioning mechanism. It has some semantic issues when used with methods other than GET though, as you note.
One thing to keep in mind is that in a truly RESTful system, versioning should not be such an issue. It should only matter in one very specific situation: when the representations of your resources change in backward-incompatible ways, but you still want to keep the same rels. So if the http://rel.myapi.com/friend resource suddenly loses its username field and gains an id field, that would qualify. But if it suddenly gains a nickname field, that's not backward-incompatible, so no versioning is needed. And if the concept of "friends" is completely replaced in your API with the concept of, say, "connection", this is not actually backward-incompatible, because API consumers will simply no longer find http://rel.myapi.com/friend links anywhere in the API for them to follow.
Yes, it's a good option. It clarifies the encoding you'll be using for payloads and lets both sides negotiate a different version of the encoding without changing the URI, as you correctly pointed out.
And yes, there's no need for a client to send a DELETE along with an entity-body. I believe it will simply be ignored by a compliant HTTP server, given that no payload data is transferred in that case. The client issues a DELETE for a URI, and the server returns a response code indicating whether it succeeded. Nice and simple! If the server wishes to return some data after a DELETE then it is free to do so, and should specify the media type of the response when it does.