On Thu, Jul 11, 2013 at 5:02 PM, Matt Franklin <[email protected]> wrote: > +1 for every one of Chris' +1s, unless otherwise noted. > > On Thu, Jul 11, 2013 at 3:47 PM, Chris Geer <[email protected]> wrote: > >> Oh boy!! :) >> >> Comments inline >> >> >> On Thu, Jul 11, 2013 at 1:20 PM, Erin Noe-Payne <[email protected] >> >wrote: >> >> > Hey All, >> > >> > As we are starting to look at the rest apis in more detail, I would >> > like to discuss and agree upon a consistent interface for our apis. >> > We currently have several developers interested in contributing to the >> > apis and the angular branch, and I would like to solidify the >> > interface, methods, response format, etc so that we can be on the same >> > page going forward. If we can agree on an api virtualization layer >> > then we should be able to build against it on the server and on the >> > angular application in parallel. >> > >> > I'll start with a proposal and look for feedback to iterate from there. >> > >> > 1. API root url >> > >> > "/api". Drop support for rpc api, move from /api/rest to just /api. >> > >> >> +1 - the only downside of this is that it prohibits implementing over time >> and requires a rip/replace approach of the whole system
Well the development in trunk can continue to happen on /rest. Angular (aka the consuming client for most of these apis) is already happening in a branch, so those changes can be treated as a rip / replace easily. >> >> > >> > 2. Media Types >> > >> > Initially support only application/json. We can revisit >> > application/xml as a nice-to-have. >> > >> >> +1 >> >> > >> > 3. HTTP Methods >> > >> > GET, PUT, POST, DELETE >> > >> >> +1 (We also need to decide if PUT can handle partial updates) >> > > I say not. That is what PATCH is for, once everything supports it: > http://tools.ietf.org/html/rfc5789 My understanding is that PUT should always be a full object replace. A quick search returns the suggestion to use PATCH, or to use POST to a subresource with a 303 response. > >> >> > >> > 4. Status Codes >> > >> > 200, 201, 400, 401, 403, 404, 500 >> > >> >> +1 >> >> > >> > 5. URL formats >> > >> > Use plural nouns (pages, people, widgets). Do not nest associations >> > beyond one level deep. For example: >> > /pages/1/regions (ok) >> > /pages/1/regions/2/regionwidgets (not ok) >> > /regions/2/regionwidgets (ok) >> > >> >> I'm not a fan of this requirement. Your example is the exact reason I'm not >> a fan actually. In all reality, regions don't mean anything outside a page, >> and region widgets don't mean anything outside of a region. Yes, they have >> IDs, but in reality, those IDs should be subordinate to the parent (so >> there should be nothing wrong with having Page 1 with regions [1,2] and >> Page 2 with regions [1,2]). I understand that's not how the DB works today >> but it's what makes the most logical sense. >> > > I agree with Chris. We should not limit to a single level. That is counter > to a few REST web service principles. > Fair enough. In this case I guess I would just be looking for consistency - will associations be infinitely nest-able. If not, what is the rule to determine where we support more or less deeply nested associations. > >> > >> > 6. Response formats >> > >> > 6a. Wrap all responses in an object. All valid (200) responses should >> > be wrapped in an object that includes a "meta" object for metadata, >> > and a "data" object for the response body. This allows us to capture >> > or extend metadata associated with a response as needed. Any metadata >> > properties should be standardized. >> > >> > Example: >> > >> > GET /people >> > { >> > meta: {count: 253, limit: 10, offset: 0, ...} >> > data: [ {id: 1, name: 'canonical', ...}, ... ] >> > } >> > >> > GET /people/1 >> > { >> > meta: { ... } >> > data: {id:1, name: 'canonical', ...} >> > } >> > >> >> This really complicates a couple things, first, it means the GET != PUT >> since the GET will include the meta data. Can we achieve this same result >> with HTTP Headers? >> We could possibly achieve the same with HTTP headers. I prefer the object approach for clarity, since custom http headers are less accessible or discoverable than object structure. I get your point, but I see the wrapped object approach used commonly in major apis. If it's clearly documented and used consistently across the entire api I don't really see an issue. >> > >> > 6b. Error objects. In the case of an error, the correct error code >> > should be returned. In addition, an error object should be returned >> > with a standardized format. Ideally including a verbose, >> > human-readable error message for developers, and an internationalized >> > readable error message for display to end users. >> > >> > GET /people/25 >> > 401 >> > { >> > developerMessage: 'Unauthorized. Access to this resource requires >> > authentication', >> > userMessage: 'Please login', >> > stackTrace: ... >> > } >> > >> >> +1 >> >> > >> > 6c. Partial responses. By default all responses, whether a list or >> > individual resource, should return a full representation of the >> > resources (not including security constraints). All endpoints should >> > support the query string parameter "fields", which accepts a comma >> > delimited list of fields to build a partial response. >> > >> >> Hmmm.....what's funny (except for the wasted work) is this is how I >> originally built the people resource. I changed it because the "fields" >> approach gets almost impossible to manage with nested elements (at least in >> Java - rewrite in Ruby anyone??). I'm open to suggestions though. I guess >> we could also make a rule that the data objects shouldn't have nested >> elements but that is a tough rule. >> > > I think the fields approach makes sense long-term; but, it is not critical. > > I don't really know what the implementation looks like. If you allow field filtering only on properties and deliver only properties (i.e. no nested objects / associations) then I would assume it is pretty straightforward. >> >> > >> > GET /people/1 >> > { >> > meta: { ... }, >> > data: { id: 1, name: 'canonical', email: '[email protected]', ... } >> > } >> > >> > GET /people/1?fields=id,name >> > { >> > meta: { ... }, >> > data: { id: 1, name: 'canonical' } >> > } >> > >> > 6d. Pagination. All requests that return a list should be paginated. >> > The query string parameters "limit" and "offset" should be used for >> > pagination. On any request in which either parameter is not set, they >> > should default to 10 and 0 respectively. >> > >> >> +1 >> >> > >> > 6e. Use camelCase for properties. >> > >> >> +1 >> >> > >> > 7. Endpoints. >> > >> > 7a. Standard endpoints: there should be standard CRUD endpoints to >> > support each rave resource. In other words, any operation possible in >> > rave should be possible through a rest api action. >> > >> >> +1 >> >> > >> > 7b. Special endpoints. In the case of certain client needs, we can >> > implement a small number of special endpoints to fulfill a specific >> > role. The primary case in point is retrieving a page for render, which >> > returns a page, its regions, its regionWidgets, and their render data. >> > >> >> +1 >> >> > >> > >> > >> > Ok, I think that's it. This is meant as a proposal only - we are >> > looking for feedback to go forward. Thoughts? >> > >>
