Every repository with this icon (
Every repository with this icon (
SproutCore REST API - The Definitive Guide
Authored By: —Greg Skluzacek [gskluzacek]
SproutCore REST API Quick Start
The SproutCore REST API allows your SproutCore data Model to interface with your backend database via your selected scripting language (PHP, Python, ASP .NET, etc.). The backend scripting examples below are given in PHP, but the ideas presented in the following sections are equally applicable regardless of the scripting language chosen.
The SC.Server class (needs to be documented) is the main class used in implementing the SproutCore REST API. Other related classes include: the SC.Record class and the SC.Store class . Please refer to the SproutCore Documentation for additional details.
This quick start section will tell you the minimum amount of information that you need to know to start using the SproutCore REST API. The code examples given are based off of the Cook Book sample application which is detailed in Sample Application section below. The CooKBook application has two data Models, a Recipe Model and a Ingredient Model. There are two corresponding database tables recipes and ingredients as well as two corresponding backend PHP scripts, recipe.php and ingredient.php. Please refer to this section for a more complete explanation. Additionally, the following properties and values used (see Key Properties section below):
In ./clients/cook_book/core.js
<pre>server: SC.Server.create({ prefix: ['CookBook'], urlFormat: '/%@?verb=%@' }),</pre>
In ./clients/cook_book/models/recipe.js
<pre>resourceURL: 'rest_cook_book/recipe.php'
dataSource: CookBook.server,
properties: [ ... ]</pre>
For additional discussion and coding examples see the Detailed REST API Methods section.
Note: There are many different ways to set the SC.Record.resourceURL and SC.Server.urlFormat properties, as well as many ways to implement the backend scripts and how to parse out input parameters and data. A specific architecture approach has been used in the code examples given below that I believes makes script maintenance easier by using one backend script, for each data Model, for handling all SproutCore REST API calls. The goal was to simplify web server setup, directory layout and to reduce the number of backend scripts used. This approach was chosen because I feel that it is the easiest and most straightforward. You, of course, have the choice of implementing the above any way you feel comfortable with.
SprouctCore REST API Quick Reference
Below is a quick reference for the SproutCore REST API. This table assumes that the properties and values mentioned in the preceding SproutCore REST API Quick Start section have been set up.
table{border:1px solid black}.
{background:#386599; color:#FFF}. |. Object |. Method |. Verb |. GET/POST |. ID Sent |
{background:#386599; color:#FFF}. |\2. Reply |\3. Example URL |
{background:#E6F0FE}. | SC.Record | commit() | create | POST | as post data |
{background:#E6F0FE}. |\2. array of record hashes |\3. http://sample.com/rest_cook_book/recipe.php?verb=create |
| SC.Record | commit() | update | POST | as post data |
|\2. array of record hashes |\3. http://sample.com/rest_cookbook/recipe.php?verb=update |
{background:#E6F0FE}. | SC.Record | destroy() | destroy | POST | as part of verb |
{background:#E6F0FE}. |\2. no response text |\3. http://sample.com/restcookbook/recipe.php?verb=destroy/123 |
| SC.Record | refresh() | show | GET | as part of verb |
|\2. array of record hashes |\3. http://sample.com/restcookbook/recipe.php?verb=show/123 |
{background:#E6F0FE}. | SC.Server | listFor() | list | GET | N/A |
{background:#E6F0FE}. |\2. key-value object hash, with properties of: records (array of record hashes), ids (array of corresponding record ids) and count (number of records, excluding any limit) |\3. http://sample.com/restcookbook/recipe.php?verb=list&order=id |
Key Properties
Before being able to use the SproutCore REST API methods, some properties on your data Model class and the application’s SC.Server object must be set.
The first task is to determine what the URL will be for calling the backend scripts. There are two properties that control the URL: the SC.Server.urlFormat property and the SC.Record.resourceURL property.
The SC.Server.urlFormat Property
Modify the modify the core.js file to set the SC.Server urlFormat: property. Find the following line:
<pre>server: SC.Server.create({ prefix: ['CookBook'] }),</pre>
and change it as follows:
<pre>server: SC.Server.create({ prefix: ['CookBook'], urlFormat: '/%@?verb=%@' }),</pre>
The above line sets the urlFormat: property so that resourceURL property (see next section) is concatenated with query string verb parameter and the REST verb value. Also, any additional parameters are concatenated onto the end of the URL.
The SC.Record.resourceURL Property
In your data Model file set the resourceURL: property as follows:
<pre>resourceURL: 'rest_cook_book/recipe.php'</pre>
The above line sets the resourceURL: property to access the recipe.php script located in the *rest_cook_boo*k directory in the web server’s root directory.
The SC.Record.dataSource: Property
The next task is to set the dataSource: property on your data Model.
<pre>dataSource: CookBook.server,</pre>
The above line sets our data Model’s (subclassed from the SC.Record class) dataSource: property to reference our application’s SC.Server object. This is used by the SC.Record methods to find your application’s server object.
The SC.Record.properties: Property
The properties: property is an array of object attribute names that will be loaded from the corresponding columns of the database. The names listed here are referred to as attributes. This is to distinguish them from the properties on the actual object itself. In your list of attributes, you should include any database fields you want to have read and/or update access to. Note there is no need to set a guid or id attribute as they will be added and/or manipulated as needed by SproutCore. Also note, there is no need to include the primary key column of the database table here as an attribute, the guid attribute added by SproutCore will serve this purpose.
<pre>properties: [
'rcpName', // full recipe name
...
...
'rcpUpdtUid', // user id who updated
'rcpUpdtDt' // update date (set by system)
],</pre>
The attribute names here should be camel case, but in the backend script they will appear in the GET and/or POST data as non-camel case.
Note: there has been some discussion on the Google group about whether or not the properties: propery is needed. I have found that the SC.Record.commit() method will not work if you do not list them in the properties: property
Backend PHP Script
Backend Script Architecture
Although there are many ways to configure the web server, backend scripts and the SC.Server.urlFormat and SC.Record.resourceURL properties, the easiest way for the backend script to handle the processing of the various REST API calls is to use one script per data Model and to pass the REST verb (and associated ID if any) in as part of the query string.
Note: Please see the first note under the SproutCore REST API Quick Start section at the beginning of this article, regarding the choices made in determining the backend script structure and how to parse the REST verb and any passed in ids.
Generated URLs
Together the above setting in the Key Properties section for the SC.Server.urlFormat property and the SC.Record.resourceURL property will generate the following URL when the SC.Server.listFor() method is called:
<pre>http://sample.com/rest_cook_book/recipe.php?verb=list&order=id</pre>
Here the REST verb, list, is passed in the query string verb parameter. The order query string parameter is sent by default and is set to id.
Examples for the other REST API methods, recipeRecord.refresh(), recipeRecord.commit() – insert new record, recipeRecord.commit() – update existing record and recipeRecord.destroy(), are as follows:
<pre>http://sample.com/rest_cook_book/recipe.php?verb=show/123
http://sample.com/rest_cook_book/recipe.php?verb=create
http://sample.com/rest_cook_book/recipe.php?verb=update
http://sample.com/rest_cook_book/recipe.php?verb=destroy/123
Again, the REST verbs, show, create, update and destroy, are passed in the query string verb parameter. Notice in the example URLs for the show and destroy verbs, the id of the record to be acted upon is passed after the value of the verb query string parameter and must be parsed out accordingly.
Sample HTTP Request Headers, GET / POST Data
Please see the Detailed REST API Methods section for samples of HTTP Request Headers, GET / POST data that would be generated for each SproutCore REST API method call.
Parsing the REST Verb and IDs
The following PHP code can then be used to extract the REST verb and any associated id(s) passed in. id(s) passed in will be stored in an array and can be iterated over using a foreach loop.
<pre>$verb = '';
$ids = array(); // initialize ids array
if (isset($GET[‘verb’]) && strlen($GET[‘verb’]) != 0 ) {
$ids = explode(‘/’, $_GET[‘verb’]); // get the verb and any id(s) $verb = array_shift($ids); // shift the verb out of the ids array // check if we have any id(s), if not try to get them from the GET data if (count($ids) == 0 && isset($GET[‘ids’]) && strlen($GET[‘ids’]) != 0) { $ids = explode(‘,’, $_GET[‘ids’]); } // check if we have any id(s), if not try to get them from the POST data if (count($ids) == 0 && isset($POST[‘ids’]) && strlen($POST[‘ids’]) != 0) { $ids = explode(‘,’, $_POST[‘ids’]); }}
Note, we first attempt to get the id(s) from the verb passed in, then from the GET ids parameter and finally from the POST ids parameter. All other query string parameters and/or other data can be retrieved from the GET and/or POST data as usual. Then in the PHP script, it’s simply a matter of using a switch statement on the REST verb to direct the processing of the request:
<pre>switch ($verb) {
case 'list':
// process REST API Verb: list
break;
case 'show':
// process REST API Verb: show
break;
case 'create':
// process REST API Verb: create
break;
case 'update':
// process REST API Verb: update
break;
case 'destroy':
// process REST API Verb: destroy
break;
default:
// process invalid REST API Verb
}</pre>
Note: a foreach loop may be needed to process the $ids array if multiple IDs are passed in.
SproutCore REST API Methods
The SC.Server.listFor() Method
The listFor() method is used to do the initial from read the database and populate the application’s SC.Store object for each data Model. To call the listFor() method you must invoke it on the application’s SC.Server object.
<pre>CookBook.server.listFor({recordType: CookBook.Recipe});</pre>
The above line will send an HTTP GET request to the URL specified by data Model’s resourceURL: property and formatted by the application’s SC.Server object’s urlFormat: property.
<pre>http://sample.com/rest_cook_book/recipe.php?verb=list&order=id</pre>
In response to the HTTP GET request, the backend script should format a SQL SELECT statement based on the various options specified in the listFor() method’s opts{} hash (see below for valid options). The backend script should then format and return a JSON encoded object in the following format: a records property – an array of records, followed by an ids property – an array of corresponding primary key ids, followed by a count property – the total number of records (excluding limit/offset that might have been included in the SQL statement):
<pre>{ records: [
{ hash_for_record 1},
{ hash_for_record 2},
{ hash_for_record 3},
etc...
], ids: [
id_for_record_1, id_for_record_2, id_for_record_3, etc...
], count: 3
}</pre>
The record property is required but the ids and count property are optional and may be included if you are using the listFor() method’s callback option. The hash_for_record_X is a hash of key-value pairs where the keys are non-camel case column names and the value is the column’s value. Additionally, a type key with a value of the data Model name must be added to the record hash, exactly as it appears in the model.js file but without the application prefix. For example if the application was called Admin and the model file was called user_object.js, then in file the Model name would appear as Admin.UserObject, so the type property would be set to UserObject. An id key with a value of the record’s primary key must also added to the record hash.
<pre>{"id": "2",
"type": "UserObject",
"first_name": "John",
"last_name": "Doe",
"email": "j.doe@anonymous.com"}</pre>
SproutCore will convert the non-camel case column names to camel case attribute names. See the Detailed REST API Methods section for an expanded example of the JSON encoded data.
There is one argument to the listFor() method, the opts{} hash. The opts{} hash is a key-value list of options. There is one required option, the recordType option. The recordType option should be set to a reference (i.e., not a string) of the data Model’s class. Here is a brief list of the other options; all of these are optional options and will be sent in the GET data except for the cacheCode option:
- order: — the order option should be set to an array of camel case attribute names. The array will be converted to a comma delimited, non-camel case list of column names and should be used in the SQL SELECT statement’s ORDER BY clause.
- limit: — the limit option should be set to a positive integer and be used together with the offset: option and should be used in the SQL SELECT statement’s LIMIT clause.
- offset: — the offset option should be set to a positive integer and be used together with the limit: option and should be used in the SQL SELECT statement’s LIMIT clause. If offset is specified, then limit should also be specified.
- conditions: — the conditions option should be set to a hash of key-value pairs of camel case attribute names and values. The hash keys will converted to non-camel case keys in the *GE*T data (hash) with corresponding values. These GET data (hash) key-value pairs should be used to build the SQL SELECT statement’s WHERE clause.
- callback: — the callback option should be set to a function or function reference. When the listFor() method completes successfully, the _callback function* is called with arguments of records (an arracy of SC.Record object of the typeRecord specifed to the listFor() method), json.count (the number of records excluding any limit/offset) and cacheCode. The backend script , may set the HTTP Response Header of ‘Last-Modified’ (not recommended, see note for cacheCode) which will be passed back to the callback function as the cacheCode value. When the listFor() method completes successfully but with the response text of ‘304 Not Modified’ the callback function will be called with null for all three arguments. When the listFor() method completes unsuccessfully (i.e., fails with a return code less than 200 and greater than 299), the callback function is not called.
- cacheCode: — Note: use of the cacheCode (as well as setting the ‘Last-Modified’ header) is not recommended as it was implemented for the Sproutit MailRoom Client. the cacheCode option should be set to the Greenwich Mean Time date/time stamp value of when the previous listFor() method was executed. The date/time stamp should be in the format of: ‘Www, DD Mmm YYYY HH:MM:SS GMT’, where Www is a 3 character day of the week, DD is a 2 digit day, Mmm is a 3 character month, YYYY is a 4 digit year, HH is a 2 digit 24 hour, MM is a 2 digit minute and SS is a 2 digit second. DD, HH, MM and SS all have leading zeros if there value is less than 10. Www and Mmm have an initial capital followed by 2 lowercase letters. The cacheCode may be retrieved from the ’Sproutit-Cache’ HTTP Request header. The cacheCode may be used in the backend script to determine if any record has an update date greater than the cacheCode. If no records have been updated since the previous listFor() method was executed, the backend script may return a response text of ‘304 Not Modified’, else the JSON encode data should be returned.
No data is sent as POST data. All options, with the exception of the cacheCode option, are sent as GET data via the query string. Here is an example listFor() method call with all options set.:
<pre>CookBook.server.listFor({recordType: CookBook.Recipe,
order: ['fieldOne','fieldTwo','fieldThree','fieldFour'],
conditions: {fieldApple: 'value1', fieldBoy: 'value2', fieldCat: 'value3'},
offset: 20,
limit: 10,
cacheCode: 'Mon, 14 Jul 2008 06:19:48 GMT',
callback: function(){alert('listFor method complete!');} }
);</pre>
And here is the resulting URL:
<pre>http://sample.com:8888/rest_cook_book/recipe.php?
verb=list&field_apple=value1&field_boy=value2&field_cat=value3&offset=20&
limit=10&order=field_one%2Cfield_two%2Cfield_three%2Cfield_four</pre>
The SC.Record.refresh() Method
The refresh() method is used to request that a specific record of the data Model object be updated from the server. To call the refresh() method you must invoke it on an instance of the data Model object (subclassed from SC.Record).
<pre>CookBook.Recipe.find(123).refresh();</pre>
The above line will first find the Recipe data Model object who’s guid: property is equal to 123 and then send an HTTP GET Request to the URL specified by data Model’s resourceURL: property and formatted by the application’s SC.Server object’s urlFormat: property. A trailing slash and the id to be refreshed will be appended to the URL.
<pre>http://sample.com/rest_cook_book/recipe.php?verb=show/123</pre>
Internally the refresh() method uses the data Model’s dataSource: property to call the SC.Server.refreshRecords() method, so make sure to have this property set.
In response to the HTTP GET Request, the backend script should format a SQL SELECT statement based on primary key id passed in. The id is concatenated on to the end of the REST verb with a leading slash, e.g., show/123. The backend script should then format and return a JSON encoded array of records in the following format:
<pre>[ { hash_for_record 1},
{ hash_for_record 2},
{ hash_for_record 3},
etc...
]</pre>
The hash_for_record_X is a hash of key-value pairs where the keys are non-camel case column names and the value is the column’s value. Additionally, a type key with a value of the data Model name must be added to the record hash, e.g., UserObject. An id key with a value of the record’s primary key must also added to the record hash.
<pre>{"id": "2",
“type”: “UserObject
"first_name": "John",
"last_name": "Doe",
"email": "j.doe@anonymous.com"}</pre>
SproutCore will convert the non-camel case column names to camel case attribute names. See the Detailed REST API Methods section for an expanded example of the JSON encoded data.
There are no arguments to the refresh() method.
The SC.Record.commit() Method – Insert New Record
The commit() method is used both to insert a new record of the data Model object to the database and to update an existing record of the data Model object on the database. The method checks the newRecord: property of the data Model object to determine if a record needs to be created or if changes to an existing record need to be committed. Before calling the commit() method to insert a new record, you must first create a new record object:
<pre>newRecord = CookBook.Recipe.newRecord(
{rcpName: ‘Mac and Cheese’,
rcpDirections ‘Bring 2 cups of water to a boil...’,
etc...},
CookBook.Recipe.dataSource
);</pre>
The above line creates a new Recipe object with the attributes specified and sets the object’s dataSource property and then adds it to the application’s SC.Store object. Next you then, invoke the commit() method on the newly created instance of the data Model object (subclassed from SC.Record).
<pre>newRecord.commit();</pre>
Alternatively the above two statements can be combined into one:
<pre>(CookBook.Recipe.newRecord(
{rcpName: ‘Mac and Cheese’,
rcpDirections ‘Bring 2 cups of water to a boil...’, etc...},
CookBook.Recipe.dataSource
)).commit();</pre>
The above line will first create a new Recipe data Model object who’s _guid: property (note the underscore) is set to a temporary value – such as @1533. Next an HTTP POST Request will be sent to the URL specified by data Model’s resourceURL: property and formatted by the application’s SC.Server object’s urlFormat: property.
<pre>http://sample.com/rest_cook_book/recipe.php?verb=create</pre>
Internally the refresh() method uses the data Model’s dataSource: property to call the SC.Server.createRecords() method, so make sure to have this property set.
In response to the HTTP POST Request, the backend script should format and execute a SQL INSERT statement obtaining the primary key id created by the database. The backend script should then format and return a JSON encoded array of records the following format:
<pre>[ { hash_for_record 1},
{ hash_for_record 2},
{ hash_for_record 3},
etc...
]</pre>
The hash_for_record_X is a hash of key-value pairs where the keys are non-camel case column names and the value is the column’s value. Additionally, a type key with a value of the data Model name must be added to the record hash, e.g., UserObject. The original _guid key (note the underscore_) and value as well as an id key with a value of the record’s primary key must also added to the record hash. The *guid* key and id key are used by SproutCore to update the temporary guid: property with the value of the actual primary key assigned by the database.
<pre>{"id": "2",
“_guid”: “1533”,
“type”: “UserObject
"first_name": "John",
"last_name": "Doe",
"email": "j.doe@anonymous.com"}</pre>
SproutCore will convert the non-camel case column names to camel case attribute names. See the Detailed REST API Methods section for an expanded example of the JSON encoded data.
There are no arguments to the commit() method.
The SC.Record.commit() Method – Update Existing Record
The commit() method is used both to insert a new record of the data Model object to the database and to update an existing record of the data Model object to the database. The method checks the newRecord: property of the data Model object to determine if a record needs to be created or if changes to an existing record needs to be committed. To call the commit() method you must invoke it on an existing instance of the data Model object (subclassed from SC.Record).
<pre>CookBook.Recipe.find(123).commit();</pre>
The above line will first find the Recipe data Model object who’s guid: property is equal to 123 and then send an HTTP POST Request to the URL specified by data Model’s resourceURL: property and formatted by the applicaiton’s SC.Server object’s urlFormat: property.
<pre>http://sample.com/rest_cook_book/recipe.php?verb=update</pre>
Internally the commit() method uses the data Model’s dataSource: property to call the SC.Server.commitRecords() method, so make sure to have this property set.
In response to the HTTP POST Request, the backend script should format and execute a SQL UPDATE statement using the primary key id sent as the id key in the POST data. The backend script should then format and return a JSON encoded array of records the following format:
<pre>[ { hash_for_record 1},
{ hash_for_record 2},
{ hash_for_record 3},
etc...
]</pre>
The hash_for_record_X is a hash of key-value pairs where the keys are non-camel case column names and the value is the column’s value. Additionally, a type key with a value of the data Model name must be added to the record hash, e.g., UserObject. The id key with a value of the record’s primary key must also added to the record hash.
<pre>{"id": "2",
“type”: “UserObject
"first_name": "John",
"last_name": "Doe",
"email": "j.doe@anonymous.com"}</pre>
SproutCore will convert the non-camel case column names to camel case attribute names. See the Detailed REST API Methods section for an expanded example of the JSON encoded data.
There are no arguments to the commit() method.
The SC.Record.destroy() Method
The destroy() method is used to delete a record of the data Model object (either an existing record on the database or a new record that hasn’t had the commit() method called on it yet). To call the destroy() method you must invoke it on an existing instance of the data Model object (subclassed from SC.Record).
<pre>CookBook.Recipe.find(123).destroy();</pre>
The above line will first find the Recipe data Model object who’s guid: property is equal to 123 and then send an HTTP POST Request to the URL specified by data Model’s resourceURL: property and formatted by the application’s SC.Server object’s urlFormat: property.
<pre>http://sample.com/rest_cook_book/recipe.php?verb=destroy/123</pre>
Internally the destroy() method uses the data Model’s dataSource: property to call the SC.Server.destroyRecords() method, so make sure to have this property set.
The backend script should format and execute a SQL DELETE statement using the primary key id sent as part of the verb separated by a slash, for example, destroy/123. No response text should be sent in reply to the HTTP POST Request.
There are no arguments to the destroy() method.
Comments
no comments yet
Note: The other major section Detailed REST API Methods is currently in process and will contain detailed informaiton regarding each of the Spoutcore REST API Methods, examples of how to call them from the FireBug console and will include complete example files that will allow you to try out the example Cook Book application (from a data Model perspective, the UI portion to display the data, i.e., the View and Control portion will be another topic for a detailed discussion). If you have any questions, please contact me by sending a message to my GitHub account






