rave-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Erin Noe-Payne <erin.noe.pa...@gmail.com>
Subject Re: [Proposal] REST API interface
Date Thu, 11 Jul 2013 21:48:23 GMT
On Thu, Jul 11, 2013 at 5:02 PM, Matt Franklin <m.ben.franklin@gmail.com> wrote:
> +1 for every one of Chris' +1s, unless otherwise noted.
>
> On Thu, Jul 11, 2013 at 3:47 PM, Chris Geer <chris@cxtsoftware.com> wrote:
>
>> Oh boy!! :)
>>
>> Comments inline
>>
>>
>> On Thu, Jul 11, 2013 at 1:20 PM, Erin Noe-Payne <erin.noe.payne@gmail.com
>> >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: 'canonical@gmail.com', ... }
>> > }
>> >
>> > 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?
>> >
>>

Mime
View raw message