Mailing List Archive

In search of RESTful CRUD holy grail
This might be a futile mission - but I think I learn something with
each recurrence of this discussion. So let's restart it once again.

CatalystX::CRUD::REST has a well described RESTful URI structure:

# POST /foo -> create new record
# GET /foo -> list all records
# PUT /foo/<pk> -> update record
# DELETE /foo/<pk> -> delete record
# GET /foo/<pk> -> view record
# GET /foo/<pk>/edit_form -> edit record form
# GET /foo/create_form -> create record form

I think everyone will agree that this is indeed REST. For some cases
there might be a slight problem with that URI schema - if you have a
string primary key - and it can have the value of 'create_form' - then
you will not know if the client is requesting a view on the object
with that id or if he wants the page with the form to create a new
object.

This leads to new variation:

# POST /foo -> create new record
# GET /foo -> list all records
# PUT /foo/by_id/<pk> -> update record
# DELETE /foo/by_id/<pk> -> delete record
# GET /foo/by_id/<pk> -> view record
# GET /foo/by_id/<pk>/edit_form -> edit record form
# GET /foo/create_form -> create record form

Of course 'by_id' could be 'instance' or whatever (perhaps the library
should let the user to choose that infix part).

Now '/foo/by_id/<pk>/edit_form' needs to make a PUT request to
/foo/by_id/<pk>. To get around the problem with requests outside of
the basic 'GET' and 'POST' repertoir we can use
Catalyst::Request::REST::ForBrowsers. But it also means that for
browser requests in the case of mistakes made in the form the action
answering that PUT request to '/foo/by_id/<pk>' needs to render the
form again with error indicators. But for non browser requests in case
of similar errors it needs to encode those errors in some other way.
This shows that 'edit_form' is only informing us how to *render* (or
serialize) the data. In the case of GET requests it informs us that we
should render the object data inside of a form and in the case of a
PUT request with errors it informs us that we should similarly put the
error data into the form. This leads to next variation:


# PUT /foo/by_id/<pk> -> update record
or return the error encoded for non-browsers
# PUT /foo/by_id/<pk>/edit_form -> update record or
return the error encoded as HTML form
# DELETE /foo/by_id/<pk> -> delete record
# GET /foo/by_id/<pk> -> view record
(for non-browsers encoded)
# GET /foo/by_id/<pk>/edit_form -> view record encoded
as an edit form

And similarly:

# POST /foo -> create new record
# POST /foo/create_form -> create record form
# GET /foo -> list all records
# GET /foo/create_form -> create record form

i.e. - here we treat 'edit_form' and 'create_form' consistently as a
path info parameter informing us about the serialization method. The
advantage of this arrangement is also that we don't need to put any
address into the 'action' parameter of the form - and let it always
submit to itself. In another variation we could move that parameter
from path info to real parameters and have uris like:

# PUT /foo/by_id/<pk> ->
update record or return the error encoded
# PUT /foo/by_id/<pk>?x-view=edit_form -> update
record or return the error encoded as HTML form
# DELETE /foo/by_id/<pk> -> delete record
# GET /foo/by_id/<pk> ->
view record (encoded)
# GET /foo/by_id/<pk>?x-view=edit_form -> view record
encoded as an edit form


Finally we need serialisation and dispatching to different methods for
the same address (/foo/by_id/) based on the method. This is covered
by Catalyst::Controller::REST. What I have not yet explored is how
to add this new serialisation method (to 'edit_form') to the
configuration of a Catalyst::Controller::REST based controller.


--
Zbigniew Lukasiak
http://brudnopis.blogspot.com/
http://perlalchemy.blogspot.com/

_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: In search of RESTful CRUD holy grail [ In reply to ]
Am 04.03.2009 um 13:25 schrieb Zbigniew Lukasiak:

> This might be a futile mission - but I think I learn something with
> each recurrence of this discussion. So let's restart it once again.
>
> CatalystX::CRUD::REST has a well described RESTful URI structure:
>
> # POST /foo -> create new record
> # GET /foo -> list all records
> # PUT /foo/<pk> -> update record
> # DELETE /foo/<pk> -> delete record
> # GET /foo/<pk> -> view record
> # GET /foo/<pk>/edit_form -> edit record form
> # GET /foo/create_form -> create record form
>
> I think everyone will agree that this is indeed REST. For some cases
> there might be a slight problem with that URI schema - if you have a
> string primary key - and it can have the value of 'create_form' - then
> you will not know if the client is requesting a view on the object
> with that id or if he wants the page with the form to create a new
> object.
>
> This leads to new variation:
>
> # POST /foo -> create new record
> # GET /foo -> list all records
> # PUT /foo/by_id/<pk> -> update record
> # DELETE /foo/by_id/<pk> -> delete record
> # GET /foo/by_id/<pk> -> view record
> # GET /foo/by_id/<pk>/edit_form -> edit record form
> # GET /foo/create_form -> create record form
>
> Of course 'by_id' could be 'instance' or whatever (perhaps the library
> should let the user to choose that infix part).
>
> Now '/foo/by_id/<pk>/edit_form' needs to make a PUT request to
> /foo/by_id/<pk>. To get around the problem with requests outside of
> the basic 'GET' and 'POST' repertoir we can use
> Catalyst::Request::REST::ForBrowsers. But it also means that for
> browser requests in the case of mistakes made in the form the action
> answering that PUT request to '/foo/by_id/<pk>' needs to render the
> form again with error indicators. But for non browser requests in case
> of similar errors it needs to encode those errors in some other way.
> This shows that 'edit_form' is only informing us how to *render* (or
> serialize) the data. In the case of GET requests it informs us that we
> should render the object data inside of a form and in the case of a
> PUT request with errors it informs us that we should similarly put the
> error data into the form. This leads to next variation:
>
>
> # PUT /foo/by_id/<pk> -> update record
> or return the error encoded for non-browsers
> # PUT /foo/by_id/<pk>/edit_form -> update record or
> return the error encoded as HTML form
> # DELETE /foo/by_id/<pk> -> delete record
> # GET /foo/by_id/<pk> -> view record
> (for non-browsers encoded)
> # GET /foo/by_id/<pk>/edit_form -> view record encoded
> as an edit form
>
> And similarly:
>
> # POST /foo -> create new record
> # POST /foo/create_form -> create record form
> # GET /foo -> list all records
> # GET /foo/create_form -> create record form
>
> i.e. - here we treat 'edit_form' and 'create_form' consistently as a
> path info parameter informing us about the serialization method. The
> advantage of this arrangement is also that we don't need to put any
> address into the 'action' parameter of the form - and let it always
> submit to itself. In another variation we could move that parameter
> from path info to real parameters and have uris like:
>
> # PUT /foo/by_id/<pk> ->
> update record or return the error encoded
> # PUT /foo/by_id/<pk>?x-view=edit_form -> update
> record or return the error encoded as HTML form
> # DELETE /foo/by_id/<pk> ->
> delete record
> # GET /foo/by_id/<pk> ->
> view record (encoded)
> # GET /foo/by_id/<pk>?x-view=edit_form -> view record
> encoded as an edit form
>
>
> Finally we need serialisation and dispatching to different methods for
> the same address (/foo/by_id/) based on the method. This is covered
> by Catalyst::Controller::REST. What I have not yet explored is how
> to add this new serialisation method (to 'edit_form') to the
> configuration of a Catalyst::Controller::REST based controller.
>

Hi,

here are my thoughts on this:

I don't distinguish between edit_form and create_form. If there is a
primary key or similar avaiable (either in the path or in $c->req-
>param)
I show the edit form, if there is no such value I show the create form.

Furthermore I would not put the form rendering in the same path as the
REST interface.

I would do something like this:


# POST /foo -> create new record
# GET /foo -> list all records
# PUT /foo/<pk> -> update record
# DELETE /foo/<pk> -> delete record
# GET /foo/<pk> -> view record
# GET /form/foo/<pk> -> edit record form
# GET /form/foo -> create record form

So now, what do we do with errors and redisplay of forms.
I would say a form should post / put / delete to the rest api.
Within that controller we can easily see if a browser is requesting
this url or a web service (see ForBrowser's looks_like_browser()).
If it's a browser we can detach to the /form/foo controller.
Otherwise we show the errors in the format of the Accept header.

moritz

_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: In search of RESTful CRUD holy grail [ In reply to ]
Zbigniew Lukasiak wrote on 03/04/2009 06:25 AM:

> This leads to new variation:
>
> # POST /foo -> create new record
> # GET /foo -> list all records
> # PUT /foo/by_id/<pk> -> update record
> # DELETE /foo/by_id/<pk> -> delete record
> # GET /foo/by_id/<pk> -> view record
> # GET /foo/by_id/<pk>/edit_form -> edit record form
> # GET /foo/create_form -> create record form
>
> Of course 'by_id' could be 'instance' or whatever (perhaps the library
> should let the user to choose that infix part).

I wrote about this during 2008 Advent:

http://www.catalystframework.org/calendar/2008/12


--
Peter Karman . peter@peknet.com . http://peknet.com/


_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: In search of RESTful CRUD holy grail [ In reply to ]
On 3/4/2009 4:45 AM, Moritz Onken wrote:
> Furthermore I would not put the form rendering in the same path as the
> REST interface.
This is my preference too. Keep the browser stuff out of the REST api.
>
> I would do something like this:
>
>
> # POST /foo -> create new record
> # GET /foo -> list all records
> # PUT /foo/<pk> -> update record
> # DELETE /foo/<pk> -> delete record
> # GET /foo/<pk> -> view record
> # GET /form/foo/<pk> -> edit record form
> # GET /form/foo -> create record form
>
> So now, what do we do with errors and redisplay of forms.
> I would say a form should post / put / delete to the rest api.
> Within that controller we can easily see if a browser is requesting
> this url or a web service (see ForBrowser's looks_like_browser()).
> If it's a browser we can detach to the /form/foo controller.
> Otherwise we show the errors in the format of the Accept header.
>
I don't like this, however. If your UI is all AJAXy, by all means let
it use the REST api directly. But for old-school HTML forms, I'd post
to the /form/foo or /form/foo/<pk> and let that controller either call
the REST api internally, or bypass it altogether.

Bruce

_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: In search of RESTful CRUD holy grail [ In reply to ]
Am 04.03.2009 um 19:30 schrieb Bruce Keeler:

> On 3/4/2009 4:45 AM, Moritz Onken wrote:
>> Furthermore I would not put the form rendering in the same path as
>> the
>> REST interface.
> This is my preference too. Keep the browser stuff out of the REST
> api.
>>
>> I would do something like this:
>>
>>
>> # POST /foo -> create new record
>> # GET /foo -> list all records
>> # PUT /foo/<pk> -> update record
>> # DELETE /foo/<pk> -> delete record
>> # GET /foo/<pk> -> view record
>> # GET /form/foo/<pk> -> edit record form
>> # GET /form/foo -> create record form
>>
>> So now, what do we do with errors and redisplay of forms.
>> I would say a form should post / put / delete to the rest api.
>> Within that controller we can easily see if a browser is requesting
>> this url or a web service (see ForBrowser's looks_like_browser()).
>> If it's a browser we can detach to the /form/foo controller.
>> Otherwise we show the errors in the format of the Accept header.
>>
> I don't like this, however. If your UI is all AJAXy, by all means
> let it use the REST api directly. But for old-school HTML forms,
> I'd post to the /form/foo or /form/foo/<pk> and let that controller
> either call the REST api internally, or bypass it altogether.

I like that idea!

_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: In search of RESTful CRUD holy grail [ In reply to ]
* Zbigniew Lukasiak <zzbbyy@gmail.com> [2009-03-04 13:30]:
> CatalystX::CRUD::REST has a well described RESTful URI
> structure: […] I think everyone will agree that this is
> indeed REST.

URI structures and REST are completely ortogonal. That statement
is about as meaningful as saying that a URI structure is Perlish.

Regards,
--
Aristotle Pagaltzis // <http://plasmasturm.org/>

_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: Re: In search of RESTful CRUD holy grail [ In reply to ]
On Wed, Mar 4, 2009 at 9:12 PM, Aristotle Pagaltzis <pagaltzis@gmx.de> wrote:
> * Zbigniew Lukasiak <zzbbyy@gmail.com> [2009-03-04 13:30]:
>> CatalystX::CRUD::REST has a well described RESTful URI
>> structure: […] I think everyone will agree that this is
>> indeed REST.
>
> URI structures and REST are completely ortogonal. That statement
> is about as meaningful as saying that a URI structure is Perlish.

OK - does that mean that you agree that someone can build a RESTful
CRUD with that URI structure?


--
Zbigniew Lukasiak
http://brudnopis.blogspot.com/
http://perlalchemy.blogspot.com/

_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: Re: In search of RESTful CRUD holy grail [ In reply to ]
Aristotle Pagaltzis wrote on 03/04/2009 02:12 PM:
> * Zbigniew Lukasiak <zzbbyy@gmail.com> [2009-03-04 13:30]:
>> CatalystX::CRUD::REST has a well described RESTful URI
>> structure: […] I think everyone will agree that this is
>> indeed REST.
>
> URI structures and REST are completely ortogonal. That statement
> is about as meaningful as saying that a URI structure is Perlish.
>

Aristotle,

You have written[0] a lot about REST. I listen when you do.

When I hear "RESTful URL" I interpret that as "URI-as-a-noun" (or more
negatively-defined, "URI-with-no-verbs").

Perhaps we should use something like "REST-friendly URL"?


[0] e.g.,
http://www.mail-archive.com/catalyst@lists.rawmode.org/msg01712.html


--
Peter Karman . peter@peknet.com . http://peknet.com/


_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: In search of RESTful CRUD holy grail [ In reply to ]
At 12:25 2009-03-04, Zbigniew Lukasiak wrote:
>This might be a futile mission - but I think I learn something with
>each recurrence of this discussion. So let's restart it once again.
>
>CatalystX::CRUD::REST has a well described RESTful URI structure:
... snip ...
> # PUT /foo/<pk> -> update record

Extremely minor point, but in a RESTish interface (unrelated to any
specific module) a PUT could also result in a create (as opposed to
an update). It's entirely possible to have a resource which an
external party issues PKs for. Then they would use PUT /foo/123 to
create (and update) the 123 resource.

(It's probably more common for resources to have their PKs issued by
the system, and be created with a POST.)


/J


_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: In search of RESTful CRUD holy grail [ In reply to ]
* Zbigniew Lukasiak <zzbbyy@gmail.com> [2009-03-04 21:45]:
> OK - does that mean that you agree that someone can build a
> RESTful CRUD with that URI structure?

Yes, because one can do with almost *any* URI structure. :-)


* Peter Karman <peter@peknet.com> [2009-03-04 23:25]:
> When I hear "RESTful URL" I interpret that as "URI-as-a-noun"
> (or more negatively-defined, "URI-with-no-verbs").
>
> Perhaps we should use something like "REST-friendly URL"?
>
> [0] e.g.,
> http://www.mail-archive.com/catalyst@lists.rawmode.org/msg01712.html

Yeah. And URIs-as-nouns is a good goal that should be encouraged,
absolutely. URIs should identify resources, not operations, that
is true.

However, putting too much emphasis on the structure of URIs is
at the same time problematic, because while it drives people away
from a terrible style, it also pushes them in the direction of
another bad style (however a less problematic one, to be sure).
They forget to put links into their output documents, instead
putting application-specific IDs in there, and then they publish
API docs that consist of tables of URI construction rules. Then
clients have to be hardwired for the interaction between those
IDs and the URI construction rules.

Which is exactly the opposite of REST.

In REST, URIs are opaque as far as the client code is concerned.
Clients only ever know what URIs to follow because they get them
as links or build them from forms or URI templates they find in
the content of server responses – *at runtime*. Not hardwired in.
The documentation of an actually RESTful API explains the format
of the server response and where to find links in it, as well as
what methods can be used against what type of link, and for what
purpose. The server URI structure is totally irrelevant. If it
changes tomorrow, and then again the day after, and again next
week, you don’t need to care; the response format is the same
(or actually, gets extended in compatible ways) so you can still
find the links in the right places and the client magically keeps
working while the server completely changes its implementation
and turns its URI structure upside down. *That* is the point of
REST: completely decoupling servers and clients from each other.

Good URI design is useful as a usability issue for humans OT1H
(bookmarkability, hackability etc are usability concerns), and
as a server implementation concern OTOH. But it’s entirely
orthogonal to the principles of REST.

Regards,
--
Aristotle Pagaltzis // <http://plasmasturm.org/>

_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/