After our awesome discussion today at the mid-west developers conference, here are the issues I'm running into trying to build a social media platform REST interface (for an iPhone app) with the current Services set up:
Current Issues:
-
Lack of consistency of interfaces
This could be broken down into separate points, but here are some specific examples:
-
POST/GET format differences
GET /{endpoint}/node/{nid} returns:
{"nid":"1","title":"this is a node", "body":"....." etc}
-- but --
POST /{endpoint}/node requires (value/pair):
{"node":{"title":"new node", "body":"...." etc} } -
POST return differences
POST /{endpoint}/node returns:
{"nid":"961","uri":"..."}
-- but --
POST /{endpoint}/taxonomy_term returns:
0 (yes not wrapped in JSON) -
Update User inconsistencies
Updating a user requires the machine calling PUT /{endpoint}/user/{uid} to know whether they are updating basic user info (mail, name, password) or profile information. For example, if you were updating mail you would PUT:
{"mail":"a@a.com", "username":"", "password":""}
If you want to update profile information:
{"category":"profile", "profile_first_name":"", ...}This should really be done in such a way that I can send Drupal only the data I want to update (let's say mail and profile_last_name) and it just updates it (if possible or you have permission, etc).
-
Field inconsistencies
In order to login, you POST:
{"username":""}
In order to create a new user or update an existing one you POST:
{"name":""}It would be nice if this was one or the other. Are there security implications with this though?
-
Contrib implements their own paging
This may be scope creep:
For example, the private messaging module (privatemsg) uses offset and limit; instead of page and pagesize
-
-
Full Views Integration
Some modules don't show up in the current services_views implementation. An example is Vote Up/Down (vud_node) 6.x-2.8.
-
HTML Markup in fields:
services_views returns body fields with HTML tags. This is using the default of FALSE for rendering theming. It leads to a general question of should tags be allowed to be part of the final output (otherwise you lose words that were bolded, etc)? Should there be a checkbox for that interface to do this?
-
Ability to only show certain fields
It would be nice to be able to select what fields get returned to the client.
-
Service Users
Let's say I have a remote machine that needs some sort of basic access (list of users, etc). It would be nice to be able to create a key that can be passed in the arguments (or proper IETF Standards) instead of authenticating as user. This would eliminate the need for service user roles and accounts strictly for remote machines to get access to limited information. Given the security implications of this, see item 1 below of what Services currently does well below:
Current Good Solutions:
Here are some awesome things that Services does well right now:
-
Ability to create separate endpoints
This is a huge plus on the current Services module. Doing this allows us to create endpoints that have limited abilities, so that if packet sniffing did occur and someone did find an interface, they could only do limited things on the interface they found if they have an account or sniff that information as well. I typically use this in combination with a service user for each type of remote machine that needs access.
-
(Limited) versioning of endpoints
Currently, you are able to (admittedly somewhat limited) create an infinite number of custom endpoints. So, if you created a basic iPhone app with some custom endpoints, and named your service awesome-v1, then later you needed to update the custom endpoints, you can create a new module, change your code, and then create a new service awesome-v2 using the new code for the endpoints. This allows you to cleanly transition as clients slowly upgrade.
Services Long Term Solutions:
- Here is what Services is trying to do in the long run for versioning of APIs:
http://drupal.org/node/1436366
WSCCI Long Term Solutions:
-
Consistent Contrib Interfaces:
This is easily the biggest issue that makes the interface professional versus look hacked together.
Somehow we need to think of a way that contrib modules can easily create custom endpoints and provide things like consistent paging, proper error formatting, etc. It would be ideal to have a review process where contrib maintainers can get feedback/help on the proper ways to implement things. At a minimum, examples of how to properly create interfaces need to be very well documented.
-
Date/Time:
We take this for granted when Drupal to Drupal talking goes on, or another system is *nix based, but is the Unix Epoch GMT/UTC time the standard format? (most likely answered by JSON-LD standard)
-
Limit endpoints by role
It would be huge and awesome if we could limit endpoints or APIs to be locked down by role or specific users. Combining this with locking down endpoints resource abilities that Services currently has would make for a safer way to open up interfaces.
-
POST: return IDs
This may go against IETF or JSON-LD standards, but if so I would support it. It's great that a remote machine receives a constant response of success or failure from proper response codes, but often I'm expecting to know not only what I submitted was created successfully, but also how I can reference it (NID, UID, TID, CID, FID etc). Often, when creating sub-items, machines are planning on using these IDs to submit with a new node.
-
multipart/form-data
Services currently does this, but the only reference (and developer I've seen using this) is here in the issue que: http://drupal.org/node/1270190. However, this is huge! I've had issues when developing for Android where the system had so little memory available that base64 encoding a file would cause the app to crash.
-
Ignore Theming
Because current Services renders through the Theme layer, the theme can inadvertently disrupt the API consistency by storing something, such as gender with 0 or 1 in the DB, and using an if statement to render it as Male or Female to users. Thus, PUT requests use 0 or 1, but GETs return Male or Female

Comments
Date/Time: We take this for
Good point. ISO 8601 based format is the way to go, and is also more human friendly than Unix Epoch. JSON-LD doesn't really recommend any date/time format but relies on existing ones, you can see an event example in JSON-LD using ISO 8601. HTML5 uses a subset of ISO 8601. Same goes for XML schema date and dateTime. Drupal 7's RDFa also output dates in ISO 8601.
If you're talking about HTTP response status codes, that's independent from the format you're using (JSON-LD, XML, …). Regarding item IDs, that's a good idea, and there is nothing in JSON-LD that would prevent you from doing it (that's not up to the JSON-LD to specify this, that's rather at the REST level).
@scor That's awesome
@scor
That's awesome research! That makes me say yes yes yes! We would have to convert all data back and forth since Drupal is storing these values as Unix Epoch C gmt in the DB. Most stuff I've written about come from the Service's maintainers general attitude of not modifying core functions (and I don't blame them!).
In general:
I've add this above, but we also need to make sure we support multipart/form-data for uploading files. When you get into Android (and now legacy iPhone App) development, you run into an issue with smaller devices running out of memory to base64 a file. Not many people seem to be using this in the Drupal Services community (that I have seen), but that doesn't mean it's not important (and something we better document/show people how to do)!
There will always be some
There will always be some trade off somewhere, each database engine stores dates differently, so chances are you'll have to convert anyways. For what it's worth, even though core stores dates as Unix time only, the date module supports several date field types including datetime and ISO. See also this issue in core.
201 Created
Per the HTTP Spec: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10
The correct behavior when creating a new resource is to send back a 201 response (Created) and include a Location header for the URI of the newly created resource, and "should" include the resource itself. So, in our case that would probably mean:
POST /node/add/page
{ The information needed to create a node. }
HTTP 201 Continue
Location: http://www.example.com/node/12
{ newly created node object, as you would get from hitting /node/12 with Accept: application/json }
Great to know about 201. I
Great to know about 201. I wonder if I can push a patch through Services to move existing implementations to this model for better transitioning when D8 comes out.
So, if in the implementation we do decide to create unique endpoints with unique resources like the current Services model, this would change to:
HTTP 201 Continue
Location: http://www.example.com/{endpoint}/node/12
{ newly created node object, as you would get from hitting /node/12 with Accept: application/json }
I think so
If you were using an endpoint setup, I believe that's correct. That said, using an endpoint wrapper like that the way services does would not qualify as REST, IMO. :-) (There are perfectly good use cases for non-REST behavior, but that would still not be REST.)
What is REST?
I think this brings up a good point! Do you have an official definition of this that you are working from and if so, where can I find it (and then I guess I have some reading ahead of me :-) )?
decent start
http://en.wikipedia.org/wiki/Representational_state_transfer
We also have someone presenting on that subject in Munich. :-)