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 Wed, 17 Jul 2013 03:53:36 GMT
On Tue, Jul 16, 2013 at 10:24 PM, Chris Geer <chris@cxtsoftware.com> wrote:
> On Tue, Jul 16, 2013 at 7:04 PM, Erin Noe-Payne <erin.noe.payne@gmail.com>wrote:
>
>> On Tue, Jul 16, 2013 at 9:20 PM, Matt Franklin <m.ben.franklin@gmail.com>
>> wrote:
>> > On Tue, Jul 16, 2013 at 12:53 PM, Chris Geer <chris@cxtsoftware.com>
>> wrote:
>> >
>> >> On Tue, Jul 16, 2013 at 10:32 AM, Erin Noe-Payne
>> >> <erin.noe.payne@gmail.com>wrote:
>> >>
>> >> > Any further discussion here? I would like to start implementing more
>> >> > of the REST APIs, as it is foundational for the entire angular
>> >> > architecture.
>> >> >
>> >> > My understanding from Matt is that the current apis in trunk are
>> >> > mostly proof of concept - they are not tested and much of the
>> >> > functionality is just stubbed. Are any of the rest api implementations
>> >> > in the code base a good working example? Is there other documentation
>> >> > we can reference?
>> >> >
>> >>
>> >> I've been working on the People resource as a "reference" of how I'd
>> like
>> >> to see them done but it's still a work in progress. I need to go back
>> and
>> >> pull out the JSONView stuff and reimplement the "fields" concept.
>> Couple of
>> >> notes:
>> >>
>> >>  - Object representations should be as flat as possible
>> >> and separate requests should be made to nested resources to get nested
>> >> details (i.e. if you have regions and regions/1/regionwidgets, the
>> regions
>> >> representation should not contain an array of regionwidgets)
>> >>
>> >
>> > I am concerned about the round trips to support this when rendering the
>> > page.  With any page that has a sufficient number of gadgets, adding to
>> the
>> > number of requests becomes problematic.
>> >
>>
>> I see that rule applying to the "standard" rest endpoints for crud
>> operations on resources. We will have some number of special endpoints
>> to support frequently used operations of clients. The major example
>> there is the page / pages for render endpoint, which will include the
>> nested regions, regionwidgets, and their rpc tokens, etc.
>>
>
> +1

So my thought is that we have the standard crud endpoints for all
individual resources. For frequently-used read operations in which we
need composite data sets we will have a small number of custom
endpoints to serve that data. Those are read-only endpoints, they will
not support create / update / delete operations.

At a time when we need one of our composite data views, such as on a
page load, the client will make a request and get the composite data -
page, regions, regionwidgets and so on. The client can decompose those
into the individual component resources which have corresponding
angular $resource services, and which talk to the standard endpoints
to support further crud operations on them.

Make sense / thoughts?

>
>>
>> >
>> >>  - All methods should return standard HTTP codes. We should document
>> this
>> >> further on the wiki to make sure we all do the same way.
>> >>  - We won't accept partial updates with PUT, we will eventually add
>> PATCH
>> >> to support that in the future
>> >>  - If the "fields" query attribute isn't included in a GET then all
>> fields
>> >> are returned.
>> >>  - What is the full meta structure we want to return?
>> >>
>> >>
>> >> >
>> >> > On Thu, Jul 11, 2013 at 5:48 PM, Erin Noe-Payne
>> >> > <erin.noe.payne@gmail.com> wrote:
>> >> > > 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