The point about REST is that it is self-describing. And ideally should be using the same URIs as the version people clicking around in Firefox or Chrome see. The API is just the XML or JSON or whatever is flavour of the week version of the HTML version.
(Or we could use embedded data—microformats, microdata, RDFa—and get rid of that distinction.)
To paraphrase the OP, "Since so many APIs can be described in similar terms, why don't we have some sort of standard that one can look at to identify how to use the API instead of letting the API speak for itself?"
When you start going down this track, you're not only making things complicated on the client's end of things. On the server side, you're having to maintain two things for the API now. First: the ruleset, ensuring it's 100% to spec lest a client fail. Second: the code generating the response in the first place.
I've built clients and servers for both RESTful and SOAPy APIs and I can say I would take REST any day.
...
> ... XML is ugly. JSON is nice since it's human-readable and light.
???
Hmmmm. Maybe JSON should just take pages from their book instead of reinventing the wheel?
Stop creating REST APIs that are only level 1 or 2 (see http://martinfowler.com/articles/richardsonMaturityModel.htm... ).
Start writing HATEOS systems where the client is coupled to the semantic rather than the syntax.
Machine parseable interface descriptions might get rid of some boilerplate but it doesn't make for a more robust client-server relationship.
The inline URLs of the web work because the consumers are humans who can deal with changes (more than just trivial URL changes, like added, removed features) and now click on this button or that button.
Software isn't that flexible, so it will be just as coupled as it is today--you're just moving the coupling around.
So this idea of a "robust client-server relationship" is a pipe dream IMO.
http://oredev.org/2010/sessions/hypermedia-apis
And he's release a Java library for creating such a client:
My experience has been that you can't communicate much through HATEOAS that's actually beneficial to a human programmer writing a client. Sure, you can add all the hypermedia links you want in your API responses, but how does that make writing a client easier? Wouldn't it just be helpful to crawlers?
Not trying to put down the idea - I want to believe, but I just haven't seen any obvious examples using it in the real world yet.
We're all calling these "hypermedia APIs" these days.
> with an associated client that actually uses it?
I have written a toy client here: https://gist.github.com/steveklabnik/2187514
You can run it against this site, written in node: http://alps-microblog.herokuapp.com/
Or this site, written in Rails: https://rstat.us/
It (should, I haven't tried it in a long while) work with both just fine. They both use the ALPS microblogging spec. yay generic clients!
As for people who have 'more real' ones: GitHub, Twilio (partially, more in the future), Balanced Payments (YC W11, iirc), Comcast (though that's internal :() Netflix has aspects, FoxyCart.
This year will be the year of examples.
Flippancy aside, maybe there's a need for a next generation of this that skips all the XML headaches after all.
I have been puzzling over this API discovery issue on my current project (building out a reporting API).
I'm starting with self documentation for developers built in, not this (admittedly admirable) goal of a machine generated API mapping layer. I think the main issue is, you're trying to generate a generic interface to something that isn't, itself, generic.
How many versions of "RESTful" have you encountered?
Building a generic interface to non-generic interfaces is the domain of software engineers. Until we have machines building both sides of this equation, there will always be a need for human intervention.
JSON has just been lucky that its user base hasn't been the same enterprise architects that ruined Java.
If the point is "stop writing procedural REST API clients and write them declaratively instead" then that advice is by no means restricted to REST API clients.
If the point is "hey, I noticed that REST API clients are another thing that we can now comfortably write declaratively" then OK.
I highly recommend you simply accept it as what it is: the way some people communicate, especially online. It's not worth your time/attention/care to think about this.
But it still looks like they pulled some rules out of nowhere and are enforcing them.
A key features of a REST API is that is self describable, in a sense, that it has a single entry point from which a generic client can automatically discover available resources and actions. APIs in the given examples are not like this, they require custom clients that are strongly coupled with an application. These are not REST APIs but RPC APIs.
How practical is that, in reality?
I know I've added the whole HATEOAS thing to my API and I am not sure if it just makes my IDs longer. Customers seem to hard-code the API entry points anyway. Everyone of course says "Oh, yeah this is cool" but when it comes to doing it given performance constraints, they don't want to start generating 10s of extra GET requests on startup to rediscover the API.
Now I can say "well not my problem" and that is what I say, except that looking back and I just don't see the practice match with the supposed theoretical advantages of the REST-ful interface.
Another issue I see happening, is the return of message bus like interface brought about by Websockets and server push optimizations it makes possible. I think REST and Websocket's channels/message bus architectures will have a battle at some point -- and one will end up dominating.
Just like AMQP is becoming a standard for message brokers, I think at some point that will be extended to the browser. Kind of like RabbitMQ has the web-STOMP plugins. I can see future hotness being just that -- message broker architecture all the way to the web client and everyone will laugh at REST-ful hype just like we are laughing at SOAP now.
Imagine you have a company that does custom mobile apps for external customers. A very popular topic, a lot of companies today want to have their own apps in addition to standard web pages.
Most of these apps are very similar (you can browse come content, purchase some service, etc.). Your company can go RPC way and create a custom interface and a custom client for each customer, with a lot of duplication and substantial maintenance cost. Or your can make larger upfront investment and create a generic REST client and then only design resources and representations for each new customer.
I have seen a few RESTful servers self-describe, (ie. GET /api/v1/ returns ['/users', '/posts']). However you can't claim this is a key feature of REST clients because there is no agreed-upon standard to have services describe themselves. HTTP is not sufficient.
If there were a real standard here, we would not have this problem. Like it or not, everybody is calling their custom API a 'REST' API nowadays, and without a real standard, nobody is wrong.
'Semantics are a by-product of the act of assigning resource identifiers and populating those resources with representations. At no time whatsoever do the server or client software need to know or understand the meaning of a URI -- they merely act as a conduit through which the creator of a resource (a human naming authority) can associate representations with the semantics identified by the URI. In other words, there are no resources on the server; just mechanisms that supply answers across an abstract interface defined by resources. It may seem odd, but this is the essence of what makes the Web work across so many different implementations.'
my $me = Facebook->new( username => 'autarch' );
$me->post_status("I'm on Hacker News writing this comment");
my $friend = Facebook->new( username => 'imaginary' );
$me->post_on_wall( $friend, "Hey buddy, I am on Hacker News writing this comment" );On the client side, I don't really care (too much) if something is a POST or PUT, I want to send a message or update a repository's metadata or share a photo.
And then, we'll need a central location to store all of these API descriptors and UDDI [2] will be back with a vengeance.
[2] http://en.wikipedia.org/wiki/Universal_Description_Discovery...
I think too many people consume REST APIs in different manners, utilizing different data in unique relations. This is the beauty of it.
Hypermedia says "oh yeah, here's some markup, look there are URIs in it". For a human user, we're like "cool, I'll try and click these, see what they do".
But software is going to want "um...okay, how to I parse this markup, and how do I generate the submissions you want? And, okay, you can change URIs, but please don't change anything else about that operation, or I will break completely. That's right, we're not really decoupled."
So, even with hypermedia APIs, AFAIK you're still going to want some marshaling to/from host language. ...and so you're back to having a spec, and coupling, you've just moved it around.
(Rant on coupling, people seem to think it's always bad and you can make it go away. Reality: you can't make it go away, and sometimes just accepting it directly is a whole lot simpler than deceiving ourselves about it's existence by over-applying abstractions.)
<form method="get" action="/me">
<input type="text" name="access_token" />
<input type="text" name="fields" />
</form>
> AFAIK you're still going to want some marshaling to/from host language.Actually, you explicitly _don't_ want this. That's what hypermedia APIs are trying to remove.
Document-generated server behavior is something we're researching as well, to possibly represent business logic. We're hoping that patterns can be found and condensed into notations, like Regular Expressions do for string-parsing. We'll post about anything that we come up with.
One of my side projects us an Ajax library which allows javascript to respond to requests (LinkJS [1]). It has a helper object called the Navigator, which is like a miniature Web Agent. It retains a context, and uses the Link header from the response to populate the navigator with relations. It works out like this:
var nav = Link.navigator('http://mysite.com');
nav.collection('users').item('pfraze').getJson()
.then(function(res) {
console.log(res.body); // => { name:'pfraze', role:'admin' ...}
})
.except(function(err) {
console.log(err.message); // => 404: not found
console.log(err.response.status); // => 404
});
The advantage is that the link header is a relatively condensed representation of the resource graph. As a result, it's not a problem to send it and process it every time. You do gain latency, but the internet is only getting faster, and caching can be used. Meanwhile, the server can rewire links without interrupting their clients.Most overused phrase right there.
My naive impression is that JSON-Schema is trying to be just like XML Schema, but in JSON. Which doesn't seem like a good thing.
IMHO, what's needed is better support for "generic" REST in programming languages and/or libraries. Objective-Smalltalk (http://objective.st) features "Polymorphic Identifiers", which make it possible to both interact directly and abstract over web interfaces.
To reference a URL, just write it down:
news := http://news.ycombinator.com
Arguments can be added without string processing: #!/usr/local/bin/stsh
#-zip:zipCode
ref:http://zip.elevenbasetwo.com getWithArgs zip:zipCode
This is a file downloader, similar to curl: #!/usr/local/bin/stsh
#-<void>scurl:<ref>urlref
fileComponent := urlref url path lastPathComponent.
file:{fileComponent} := urlref value.
For abstraction, you can build your own schemes, either directly in code or by composing/modifying other schemes. For example, if I want to look up RFCs, I can define the rfc scheme: scheme:rfc := ref:http://datatracker.ietf.org/doc asScheme
Or I can compose schemes so the rfc scheme looks in a bunch of different places (memory, local directoy, several http/ftp servers).SOAP was insane and its counterpart, WSDL (which is really the part that is most comparable to this idea), was even more insane.
But, the basic premise was not bad. It was the execution which sucked by trying to account for every situation, adding namespaces, etc. And if you ever worked with language libs designed to interface with SOAP/WSDL, it would make you slap a bunny.
With this idea, however, adding an optional JSON-based descriptor language could be helpful. Key would be to keep it simple, allowing the bare mnimum number of data types, with one simple array structure for collections. Allow object definitions with an infinite number of nesting levels, and that would be it. I wouldn't even get into optional vs required stuff, validation, etc. That stuff should stay at the application level. Why stuff it into the interface layer?
From there, it would be easy to develop libraries to generate clients in any language for any API just by feeding it the JSON descriptor. Or (as I think the author intended) just use one universal client that any app can use. For languages that aren't strongly typed anyway, the latter would be fine.
Someone mentioned that it would require the server side devs to keep the descriptor in sync with the code. No biggie for apps that already offer client libs in different languages and must keep them up to date anyway. Not to mention there should be some doc that needs to be kept in sync (REST is not typically self documenting in reality).
In any event it wouldn't be required. What would be the harm in creating a standard for those apps that choose to use it?
That's the path I've been using in all projects lately - because frankly - I don't want to deal with a bunch of different API clients for Twitter, Facebook, Soundcloud, Instagram or whatever sites it is that I integrate with - all those different syntaxes and all that duplicated code etc doesn't help me - I want all of their individual differences hidden away for me and colleagues behind a single well known syntax which I myself can extend to expose the resources and methods that I need - like if I need it a method for posting a photo for the API:s that support that and so on.
My advice today would be: Pick a good HTTP client, preferably with good OAuth support, and then build your own extendable API-client on top of that and integrate all the different API-resources you need with that client whenever you need them.
Another example: I wrote a client that returns a queue message. Attached to that message are some helper methods for deleting, releasing, and touching the message. It makes your code cleaner and easier to understand.
I'm curious how this works in practice. What about authorization and parts of the API that are only available to certain users, does the client generator need to be authenticated? Are there standards for describing the meta-data associated with URLs (validation, optional parameters, etc.)?
Both are currently used by Google's public APIs to auto-generate clients. Ruby/Python clients load the schema docs at runtime and do method_missing magic, Java/.NET clients generate static typed libraries periodically.
http://static.springsource.org/spring-android/docs/1.0.x/ref...
RPC FTW ;)
Actually not having a "universal" spec helps: it forces every provider to give some thought to how to make his API as lean as possible. Hopefully.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.google</groupId>
<artifactId>google</artifactId>
<version>5</version>
</parent>
<groupId>com.google.api.client</groupId>
<artifactId>google-plus-java-webapp-starter</artifactId>
<packaging>war</packaging>
<version>1.0.0</version>
<name>google-plus-java-webapp-starter</name>
<description>
Web application example for the Google+ platform using JSON and OAuth 2
</description>
<url>https://code.google.com/p/google-plus-java-starter</url>
<issueManagement>
<system>code.google.com</system>
<url>https://code.google.com/p/google-plus-java-starter/issues</url>
</issueManagement>
<inceptionYear>2011</inceptionYear>
<prerequisites>
<maven>2.0.9</maven>
</prerequisites>
<scm>
<connection>
scm:hg:https://hg.codespot.com/p/google-plus-java-starter/
</connection>
<developerConnection>
scm:hg:https://hg.codespot.com/p/google-plus-java-starter/
</developerConnection>
<url>
https://code.google.com/p/google-plus-java-starter/source/browse/
</url>
</scm>
<developers>
<developer>
<id>jennymurphy</id>
<name>Jennifer Murphy</name>
<organization>Google</organization>
<organizationUrl>http://www.google.com</organizationUrl>
<roles>
<role>owner</role>
<role>developer</role>
</roles>
<timezone>-8</timezone>
</developer>
</developers>
<repositories>
<!--
The repository for service specific Google client libraries. See
http://code.google.com/p/google-api-java-client/wiki/APIs#Maven_support
for more information
-->
<repository>
<id>google-api-services</id>
<url>http://mavenrepo.google-api-java-client.googlecode.com/hg</url>
</repository>
<repository>
<id>google-api-services-drive</id>
<url>http://google-api-client-libraries.appspot.com/mavenrepo</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<configuration>
<contextPath>/</contextPath>
<systemProperties>
<systemProperty>
<name>configurationPath</name>
<value>./src/main/resources/config.properties</value>
</systemProperty>
</systemProperties>
</configuration>
</plugin>
</plugins>
<finalName>${project.artifactId}-${project.version}</finalName>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<netbeans.hint.deploy.server>gfv3ee6</netbeans.hint.deploy.server>
<project.http.version>1.13.1-beta</project.http.version>
<project.oauth.version>1.13.1-beta</project.oauth.version>
<webapi.version>6.0</webapi.version>
</properties>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>13.0.1</version>
</dependency>
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-drive</artifactId>
<version>v2-rev53-1.13.2-beta</version>
</dependency>
<dependency>
<!-- A generated library for Google+ APIs. Visit here for more info:
http://code.google.com/p/google-api-java-client/wiki/APIs#Google+_API
-->
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-plus</artifactId>
<version>v1-rev22-1.8.0-beta</version>
</dependency>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>1.13.2-beta</version>
</dependency>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client-servlet</artifactId>
<version>1.13.1-beta</version>
</dependency>
<dependency>
<!-- The Google OAuth Java client. Visit here for more info:
http://code.google.com/p/google-oauth-java-client/
-->
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client</artifactId>
<version>1.13.1-beta</version>
</dependency>
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client-servlet</artifactId>
<version>1.13.1-beta</version>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client-gson</artifactId>
<version>1.13.1-beta</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client</artifactId>
<version>1.13.1-beta</version>
</dependency>
<!-- Third party dependencies -->
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client-jackson2</artifactId>
<version>1.13.1-beta</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>${webapi.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>javax.jdo</groupId>
<artifactId>jdo2-api</artifactId>
<version>2.3-eb</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>1.3.9</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>xpp3</groupId>
<artifactId>xpp3</artifactId>
<version>1.1.4c</version>
</dependency>
</dependencies>
</project>