Hacker News new | ask | show | jobs
by CraigJPerry 4441 days ago
If REST client libs didn't suck, this wouldn't be needed.

It's a load of effort to correctly consume from a RESTful webservice currently.

I should be able to get going in my REPL with something like:

    >>> from rest import client
    >>> proxy = client(url)
    >>> print proxy.resources
    ['foo', 'bar']
    >>> help(client.foo)
    Help text from the rest service...
    >>> client.foo(123)
    321
    # repeat calls transparently handle caching
9 comments

Have you tried Hammock? It fakes it very well:

  >>> from hammock import Hammock as Github

  >>> # Let's create the first chain of hammock using base api url
  >>> github = Github('https://api.github.com')

  >>> # Ok, let the magic happens, ask github for hammock watchers
  >>> resp = github.repos('kadirpekel', 'hammock').watchers.GET()

  >>> # now you're ready to take a rest for the rest the of code :)
  >>> for watcher in resp.json: print watcher.get('login')
  kadirpekel
  ...
  ..
Your "resources" example only works if there's a /resources endpoint, but client.foo(id).GET() works just fine.

https://pypi.python.org/pypi/hammock

This is the closest i've seen. Excellent link, thanks!
Thanks! I though I was the only one.. The RESTful API is designed to be really simple for clients. If you designed yours well, your users won't need your client API.

For instance, on iOS we have the wonderful RestKit Client[0]. If you create your own client it means I would have to write specific cases for your API and miss all RestKit features. Don't get me wrong, I could still use the RestKit with your API, but when I see an API client available, I always think "this API may be bad designed, it needs specific code".

[0](https://github.com/RestKit/RestKit)

A few things off the top of my head which are different across many RESTful APIs which prevent a unified client from being possible:

- Referring to other data paths in the API in a standard format

- Authentication. Shy of OAuth, it is totally different on each API. Most APIs avoid OAuth because its a PITA

- Partial PUTs vs PATCH vs sub-resources

All sorts of minor things are different across rest APIs, and that results in client libraries needing to be widely different.

We are in need of a standardized API format which we can build compatible server & client libraries against. Something like SOAP for the JSON era. There are a few out there, but none have really gone anywhere. Are any of these extended-REST wrappers in production by any big companies? I'd love to be corrected.

If not, maybe a high-profile company with a really nice API design could publish a standard on their API structure and refactor out their transport code to provide us with these libraries. If successful, they could be known for introducing a widely-used transport layer for the web industry.

Authentication shenanigans and needing to use PATCH in a sane way are much better reasons for using a custom client than all six reasons listed in the original article...

I don't get what you mean about data paths, though. Even if you had a hypothetical good REST client you'd still want a single point to do the URL string assembly to save typing and do some validation but this doesn't really require a whole different client.

One example I know of is OData[1]. The URI structure is standardized, all query operators are standardized, and the media type is JSON based on Atom. They also have one of the nicest batch request support I've seen (using multipart/mixed content type).

Needless to say, OData is a standard pushed by Microsoft, so the ecosystem for libraries is is mostly .NET, although they provide a JavaScript library as well (datajs).

[1] http://www.odata.org/

This kind of thing is why I'm predicting a return to WS-*, or else a reimplementation of it on top of REST. The XML backlash was mostly correct but having a standard, well-specified way of creating HTTP APIs and generating clients for them from a single endpoint is a baby we threw out with the bathwater.
The horribly-named HATEOAS is one possible solution: http://timelessrepo.com/haters-gonna-hateoas
Maybe, if the library support is there. Right now HATEOAS seems to mean replacing your domain-specific action tags with boilerplatey <link rel=...> and not a lot else.
Yes, if HATEOAS were actually done, all you'd need to do is have libraries for handling the content types used in the API, and with a suitable generic client library everything else would come for free.

But virtually no one does that with their APIs, and virtually no one builds client libraries on the assumption that services will do that, so there's something of a chicken-and-egg problem.

I wonder how to implement that. This kind of stuff is generated from WSDLs in C#/VS, but it's an interesting idea to do it in a REST service.
Like WADL?

https://en.wikipedia.org/wiki/Web_Application_Description_La...

I guess I really don't get REST, shipping a REST client for a specific API is a thing. If you're gonna provide a client, who cares if it's really REST after all?

https://pypi.python.org/pypi/wadllib/1.1.4 looks quite complicated. I'd really like to be able to do some_object.some_remote_method_call(parameters) like the parent.

I guess REST is there to make it easier for other people to implement your API. SOAP was hard without libraries, I can hit your REST endpoint with curl.

"I can hit your REST endpoint with curl."

I must admit that was my initial reaction when I saw the title of this article - I tend to regard curl as the universal RESTful API client. I don't think I've written or consumed a RESTful API where I didn't spend a fair amount of time interacting with the system through curl.

cURL works fine for RPC-style or non-RESTful clients too. Instead of "-XPUT" you make your URL end with /foo/addfoo or something. And who cares if the FooID is in the URL or in postdata? In fact, cURL makes it easier to add another data parameter than change the URL.
Its very difficult if not impossible to make a client which behaves correctly for all services. A simple example to demonstrate my point: the Facebook FQL API limits the maximum size of a result-set at any point in time to 5000. Therefore, you have to work around that limitation in a very specific way that no generic client can handle properly. That is partly why the advice given in the OP is so good: because different services can and do handle each of those points in a different way.
Agreed; REST clients/frameworks should have ways of handling all six of these problems--none of them are specific to a particular API.

If the client lib has an API-specific way of handling "caching", say, then the number of ways of handling "caching" is O(n) in the number of APIs you consume. If you use the REST APIs directly, then the HTTP's standard, debugged, documented caching mechanism is the only one you ever need to know.

Maybe I'm being a bit dim, but could you explain what "client.foo(123)" does in this client - it appears to be a resource and 'callable' - so is this doing a GET on that resource, what about other methods?
Yeah good question.

I think GET would be a reasonable default here, perhaps 123 is a query string parameter.

For update / POST, create / PUT etc. it's less clear cut in my example language of Python since all we have is a callable, we don't have constructs such as "new" to map behaviours to (repurposing "del" isn't possible).

Perhaps there's an extra parameter:

    client.account(id=123, name="something", _method=PUT)
Maybe there's a postfix operation:

    client.account(id=123, name="something").PUT()
Maybe the verbs are seperate:

    from rest import client, PUT
    PUT(client.account(id=123, name="something"))
I think the postfix system probably makes most sense. I'm sure there's other ways i could come up with.