darael: Black lines on a white background.  A circle, with twenty-one straight lines connecting seven equally spaced points. (Default)
Darael ([personal profile] darael) wrote in [site community profile] dw_dev2014-05-20 03:29 pm
Entry tags:

YAAPI: the sloooow continuation

OK, so I'm disgustingly slow at this. Shoot me.


I made a comment on my original post on this topic significantly after people probably stopped checking it. It was as follows:

I haven't abandoned this; things have just been a bit full-on.


Quick query: Even with the assumption that YAAPI would be primarily JSON-based there are two major ways it could be implemented: The entire action could be encapsulated in an HTTP POST, or as much as possible could be placed in HTTP headers (exempli gratia, a JSON object containing OAuth tokens as the content of the HTTP Authorization: header, and use of (again, exempli gratia) things like the HTTP DELETE method if deleting an entry or comment rather than overloading POST).


My inclination is to go with the latter, but there may be good reasons to do everything with POST requests.


Thoughts?



I did garner one response, though, from [personal profile] fu. It read:


Hmm. I don't want to get bogged down in details, but these two examples feel like separate things, and the answer to one doesn't necessarily affect the other.

e.g., auth -- I'm strongly in favor of keeping it as simple as possible. Maybe even as simple as /v1/foo/1234?token=xxxx

The difference would be most obvious when testing GET requests, but even with POST requests, sometimes lining things up just right can get fiddly, I'd like to avoid that as much as possible.

HTTP DELETE sounds right. I don't feel very strongly about that vs an empty HTTP POST -- other than that I can imagine a hypothetical where someone accidentally does the latter ;) So using DELETE sounds reasonable.

I guess PATCH vs POST is another possibility?


...so I suppose I might as well answer here, as well as hoping others will be interested in chipping in.

There are multiple possible models here.

One is to use GET wherever possible, and POST everywhere else, with all call-related information in the arguments (URI components for a GET, POST-body for a POST), and quite possibly a single URI for all API calls (say, {siteroot}/api/v1). This seems to be what a lot of APIs do, but it's both redundant when there are more HTTP verbs than that already and arguably a violation of the RFCs that define the HTTP standard. It's also going to have a negative effect on the RESTfulness of an API, especially with the single-URI scheme, which implies more of an RPC model. On the other hand, it is likely to work with even the simplest of HTTP client libraries.

Option two is even more uniform than that: POST for preference except where GET is clearly the superior option, and all information relevant to a POST-based call to be transmitted in JSON in the body. Obviously this requires that the Content-Type: header be set to application/json (but this is no more of a problem than the occasional need to set a multipart Content-Type in the first option). It also ignores perfectly good existing standards, which I wouldn't be entirely happy with. Nevertheless, a brief practical description: Deleting a post would be done with a POST to the API endpoint URI, with a JSON-encoded body something like { "action": "delete", "auth": {}, "target": {"type": "entry", "id": ""}}. An edit would be similar, but the "action" would now be "edit", and there would be an additional attribute, itself an object with one attribute for each attribute to be changed on the target.

Next option is to go to the other extreme. Use whichever HTTP verb is most appropriate for what the call actually does, with headers used where it makes any sense, the URI itself used to encode some of the information, and the body for where potentially-very-complex data is transmitted. As an example, the obvious method to delete an entry in such a scheme would be to send an HTTP request something like the following:

DELETE /nnnnnn.html
Host: [Unknown site tag].dreamwidth.org
Authorization: dw-oauth

...and an edit would use HTTP PATCH on the same URI, with the same Authorization header, and the body of the request to be a JSON object with the attributes to be altered just as in option two. This scheme makes maximum use of what's already defined for HTTP, at the potential expense of some consistency between different types of call. I confess that left entirely to my own devices this is probably the model I'd go for, but this post is happening because I'm entirely willing to be converted.

There is also a possible "hybrid" approach: use not one but several (presumably meaningfully-named) API endpoint URIs, each of which responds only to either GET (and presumably HEAD) or POST, rather than reusing existing resource addresses for operations on said resources. Commands would still be JSON-encoded, without use of less-common HTTP verbs or any header manipulations, but some of the complexity would be moved to the URI.


So, you 'orrible lot, as potential users of such a new API, how do you think it should work?
mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)

[staff profile] mark 2014-05-20 06:41 pm (UTC)(link)
I have lots of opinions on the subject (sorry I didn't respond to the first post), and will try to respond this week.

In short though, I care little about "RESTfulness" or RFCs, but more about what's easy to use and work with. There are some pretty common patterns out there for a successful API that developers are finding easy to work with, and these are ones I want to model after:

https://stripe.com/docs/api
http://www.twilio.com/docs/api/rest

Both of these are very good APIs. Some points I think that they get very right:

1. URLs should be of the form 'api.dreamwidth.org/v1/DOMAIN/RESOURCE/ID' (so like, api.dreamwidth.org/zorkian/posts/1 or something, maybe). The URLs refer to the thing being mutated/done, but shouldn't include any content.

1a. We could also go with 'domain.dreamwidth.org/api/v1' which would probably be reasonable and might be better. I am not sure if it's better to have one domain for API usage or use user domains. (I suppose having one domain is nice because then we can use the API from the browser without dealing with CORS/etc?)

2. HTTP verbs should be used appropriately (GET, POST, DELETE).. I'm flexible on PUT/POST arguments.

3. Authentication should be lightweight. I like the API key approach for people who want to implement their own tooling (OAuth is kind of heavy to support), but OAuth is perfect for web sites that want to implement things that use the Dreamwidth API.

4. I'm generally against using headers for things (except the Basic auth header for an API key style authentication token).

5. JSON is the dominant encoding format and we should use it as the body of the request.

I think those are my immediate thoughts about APIs in general. I have spent a decent amount of time talking about APIs and thinking about them for Dreamwidth, and I even started implementing a REST/JSON API at one point. Didn't get very far though.

But hi! Welcome. Nice to see you. :)
necaris: me looking dapper at a wedding (Default)

[personal profile] necaris 2014-05-20 07:32 pm (UTC)(link)
I too have lots of opinions on this subject, but mine are generally less justified ;-)

1: 1 > 1a, because CORS is a pain in the behind and IMHO it's clearer to do DOMAIN/RESOURCE/ID

3/4: The OAuth standard suggests using an Authorization: header to hold an access token, which I'm +1 on because it's pretty straightforward and easy to understand. It'd be great if something else was supported as well but Authorization is a standard header and IMHO since the standard headers are available they should be used.

I would love to help out with this if possible, headspace and time constraints permitting :-)
mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)

[staff profile] mark 2014-05-20 07:52 pm (UTC)(link)

Re: 3/4, yeah, for sure, the Authorization header is definitely what you use for Basic auth. I believe that we're on the same page there. I want to not add headers like the Amazon API which has you add a bunch of X-This and X-That headers, I think that's a pattern I would prefer to avoid.

Getting help is awesome. I'm happy to help too, particularly with code reviews and design and direction. Code too, as much as I can. :)

exor674: Computer Science is my girlfriend (Default)

[personal profile] exor674 2014-05-21 12:10 am (UTC)(link)
I've got the OAuth implementation done, just not committed or live yet.

The Authorization header is the main way other pages can use OAuth in my implementation.