[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Web Services API: URL naming proposal



Hi, all,

Been quite swamped with work recently, sorry for the delay.

Having just designed a REST API for a client, have some fresh
perspective. Below...


On 11/18/2011 05:52 AM, Chris Travers wrote:
> Figure i will wade in here.  Hopefully others will follow.
>
> On Sun, Nov 13, 2011 at 10:45 AM, Erik Huelsmann <..hidden..> wrote:
>> Hi all,
>>
>> Before going into detail how to interact with resources, I think we
>> need to come up with a resource (URL) naming scheme which works for
>> our application. There may be some things to consider, so, I thought
>> I'd put out a proposal for review. From there, we could go into the
>> more technical details of the actual interaction.
>>
>> We agreed to start with the functionality which has been rewritten
>> since our SQL Ledger fork, known as 'new code'. One of the notable
>> areas this has happened to is the way customers, vendors and employees
>> are now recorded. I'll direct my proposal there first.
> That's probably wise since other web services would habe to be rewritten later.
>> Before going into the details of the customer/vendor store, there are
>> a few general subjects to address:
>>
>> 1. The base address mentioned by John was something like
>> 'http://myledger.com/ledgersmb/store/'. However, if we assume that the
>> login address of the 'http://myledger.com/ledgersmb/login.pl', there
>> may be an issue with the '/store/' part. Can we support that on all
>> servers, or does that not matter? Do we allow '/store.pl/' or any
>> other URL? ie. do we not consider that part of the API, other than
>> that it is a prefix?
> i don;t think that would be a problem. I don't like store though
> because it is pretty unclear.  I would suggest a base path of:
>
> http://myhost/ledgersmb/webservices/company_name/
>
> Then http auth headers can be used for the rest.

I'm starting to like /api/ or /rest/ for the path that identifies the
web service. And also adding an API version number to that endpoint, so
that it becomes possible to change the API in the future while still
maintaining an old version (should the organization wish to...)

On authentication, yes we can use http auth headers, but do we want to
explicitly require a session token, too? We're starting to delve into
OAuth -- which adds a layer of complexity but also can take away the
need for the remote system to collect the password at all. This seems
like a good option to support.

So: I would suggest for login, something like:

POST http://myhost/ledgersmb/api/1.3/user/login

... with credentials in the body. Content-type header should specify the
format of the post body -- would be nice to support multipart-form in
addition to JSON and XML.

That initial post should send an authentication session cookie to be
used in future requests.
>> 2. Since companies are separate databases, where do we put the name of
>> the company in the URL? <prefix>/store.pl/<company>/<etc...>?
>>
> What do you think of the above proposal?

I suggest we include the company in the body of the login, and then it's
represented in the session cookie. If an external application needs to
work with multiple companies, it can manage multiple session ids.
>> Are there other items to consider before we go to specify the web
>> service behaviour of our natural/legal entity storage paradigm?
>>
> I think the key issue is designing a logical entity hierarchy for
> things. Otherwise I don't see any other issues that will come up in
> this preliminary stage.

So one thing is identifying supported formats for the data -- I suggest
we support JSON, multi-part form (e.g. URL encoded like a regular form
post) that returns HTML, and some relatively simple XML. Type can then
be specified via "Accept" header, and also by adding a suffix to the
URL. For example:

http://myhost/ledgersmb/api/1.3/customer/224.json
http://myhost/ledgersmb/api/1.3/customer/224.xml

POST/PUT type should get specified by Content-Type header.

I suggest we start with the base entity structure that mostly maps to
the database structure, then add "sugar" path shortcuts to make this
easier to use. e.g. all of the below might map to the same item:

http://myhost/ledgersmb/api/1.3/entity?eca=224
http://myhost/ledgersmb/api/1.3/entity/eca/224
http://myhost/ledgersmb/api/1.3/eca/224
http://myhost/ledgersmb/api/1.3/entity/eca/customer/224
http://myhost/ledgersmb/api/1.3/customer/224
http://myhost/ledgersmb/api/1.3/customer?meta=557 (which might redirect
to the actual eca id)


... then add more "sugar" methods to get related items:

http://myhost/ledgersmb/api/1.3/customer/224/invoice?poststartdate=2011-01-01&poststartoper=gte

... might return a collection of invoice objects for customer 224 with a
post date greater than/equal to January 1, 2011.

http://myhost/ledgersmb/api/1.3/customer/224/invoice?status=open

... might return all open invoices for the customer.

To create a new customer, you would POST customer data to

http://myhost/ledgersmb/api/1.3/customer

... and it would come back with the ECA id set, and a Location: header
set with the resource URL for that item.


For the API I was just working on, one other key consideration was
removing a lot of duplicate fields from the object -- the way Drupal
builds objects (especially with the variety of modules we used on this
project) resulted in the same data appearing multiple times. This is
very confusing for non-Drupal developers to figure out, so we are
creating simplified versions for each type of object that are much
flatter and contain only a single copy of each field, wherever it comes
from. It then merges data from PUT requests into the appropriate spot of
objects to update before saving.

So we should pay attention to the structure of the objects we're
returning -- and if they are more complicated than necessary, for the
sake of external developers I suggest we add a mapping layer to simplify.


My thoughts, anyway!

Thanks for kicking this off...

Cheers,
John Locke
http://freelock.com