Documentation‎ > ‎

HTTP Interface

The Persevere Server persisted data can be accessed and manipulated with a simple and intuitive JSON REST interface. You can store, retrieve, and modify persisted data using standard REST semantics with the following HTTP methods. Clients communicate through these standard HTTP methods.

Note - data is transmitted by default in the application/javascript format, allowing things like dates and functions (not included in the JSON spec) to be transmitted nicely.  If you're looking for just JSON, add an HTTTP Accept header with a value of application/json.  Dates and functions will be serialized in this case.

GET

The GET method can be used to retrieve an object by id, a value by path, or a result set by JSONQuery. To retrieve an object by id, simply use the url corresponding to the object. For example to retrieve the /Customer/1 object:

GET /Customer/1
-->
{"id":"1",
"name","John",
...
}

To retrieve a value, simply use property path access appended to a starting id. For example:

GET /Customer/1.firstName
-->
"John"

You can use more complex paths:

GET /BlogPost/1.comments[0]
-->
{"id":"1",
"lastName":"customer",
"phoneNumber":"a phone?",
"address":"somewhere",
"created":"2007-10-23T14:40:18Z",
"firstName":"first",
"comment":"this is great"
}

You can also use JSONQuery queries to retrieve query result sets from the server:

GET /Customer/[?firstName='John']
-->
[
{"id":"1",
"lastName":"customer",
...
}]

You may use the HTTP Range header to specify a range of items to retrieve. This is recommended for paging through results. Persevere supports a range unit "items" which identifies the items to be retrieved by index. For example one one could use the following request to retrieve the first twenty items of the Customer/ table:

GET /Customer/ HTTP/1.1
Range: items=0-19
Other range values could include:
Range: items=10-19  # items 10 through 19
Range: items=0-49 # the first 50 items
Range: items=100- # items at index 100 and above
Persevere will return a Content-Range header indicating the items returned, and the total number of items in the collection.
You can use a media type parameter collection in the Accept header to indicate that the collections returned by queries be returned as an object with a specified property holding the collection as an array, rather than the array being the root of the response. In this case, the object will include a property totalCount that will indicate the total number of items. The value of the collection parameter will indicate which property holds the collection array. For example:
GET /Customer/ HTTP/1.1
Accept: application/javascript; collection=items
response:
{
totalCount: 43,
items:[
    {id:"1",...
]
}

POST

You can use the HTTP POST method to create new items in the table. For example:

POST /Customer/
{"firstName":"new",
"lastName":"customer"
}

This will add a new customer to the list of customers. If successful, the server will return the newly created object with the newly assigned id:

{"id":"4444",
"firstName":"new",
"lastName":"customer"
}

The location of the new resource (corresponding to the assigned id) is available in the Location header. If the append was not successful, the HTTP status code will indicate the failure

PUT

You can modify an existing object by using PUT method to put a new set of values to existing object id. For example we could modify Customer/1 with this request:

PUT /Customer/1
{"id":"1",
"lastName":"new last name",
...
}

You can also use the PUT method to modify individual property values by using path references. For example we could just change the lastName property of the Customer/1 by using the following request:

PUT /Customer/1.lastName
"another new last name"

If the PUT was not successful, the HTTP status code will indicate the failure

DELETE

You can delete existing objects by using the DELETE method with the URL for the object. For example we can delete the /Customer/1 object by this request:

DELETE /Customer/1

You can also delete a single property by path. For example we could remove the comment property:

DELETE /BlogPost/1.comment

If the DELETE was not successful, the HTTP status code will indicate the failure

Object URL Determination

The URL for an object can be determined from the id property by using the standard rules for relative urls. For example if we request:

http://mydomain.com/Customer/
-->
[
{"id":"1",
"firstName":"first",
...
}
]

We can determine the URL for the first object from id property (which has a value "1") in combination with the context of the requested URL (http://mydomain.com/Customer/). The inferred URL would therefore be http://mydomain.com/Customer/1.

Referencing

Persevere supports JSON Referencing which can be used to describe complex data structures and for referencing other objects in the Persevere database or even cross-site references. If an object is lazy loaded, circularly referenced, multiply referenced, or on a foreign site, Persevere will use a reference to the object. References are defined as an object with a property "$ref" with a value that represents the id or path of the target object. References follow the same rules as ids for relative URL inference and path resolution. Therefore a reference to an object that has not been transferred in the current response could be denoted:

GET /Customer/1
-->
{"id":"1",
"address":{"$ref":"/Address/1"},
...
}

This denotes that the address property should have a value of the object with the id of Customer/1 (using relative URL inference). A client could do a GET on /Customer/1 to retrieve this object. Circular reference are also described using references:

{"id":"1",
"referenceToSelf":{"$ref":"1"},
}

References can also have paths in them, which are used by default to reference arrays:

{"id":"1",
"myArray":[1,2],
"duplicateReferenceToMyArray":{"$ref":"1#myArray"}
}

References can be made to absolute URLs as well:

{"id":"1",
"foreignReference":{"$ref":"http://otherdomain.com/Class/1"}
}

Sequencing Control

HTTP is a non-deterministic protocol in regards to ordering when multiple TCP/IP connections (as browsers usually do). If you want to ensure that you requests are sequentially processed in the correct order, you should include an Seq-Id header with each request. For each request, the header value should be incremented one. It is recommended that you use this header in conjunction with the Client-Id header which indicates which page the request is made from. The Client-Id should be randonly generated string. For example:

Client-Id: 35523532
Seq-Id: 3
This would indicate that the request should be processed after the request with a sequence id of 2 and before the request with a sequence id of 4 for the client with the given id.

Transactions

To indicate transactional processing, the Transaction header is used. If the Transaction header is omitted it is always assumed that the modifications made by the current will be committed. If you want the current modification to be included in a transaction set, and you do not want the transaction to be committed until a later request, you should set the Transaction header to "open" like:

Transaction: open
It is highly recommended that you use the Transaction header in conjunction with the Seq-Id and Client-Id headers for deterministic ordering so that the commit takes place after the correct modification.

Data Integrity/Optimistic Locking

With Persevere you can make conditional requests in order to assert the data state assumptions prior to modifying data. This can be used to provide simple optimistic locking, preventing inadvertent modification of the wrong version of an object. Or it can be used for more sophisticated assertions based on knowledge of the current state of various objects. To make a conditional request, you put a JSONQuery expression in an If header. If the expression in the If header evaluates to false, the request will be rejected with a 412 status code. For example:

PUT /Customer/1 HTTP/1.1
If: Customer/1.version=3
This PUT would be rejected if the version property does not equal 3.

Live Data - Comet Support

Persevere supports Comet style connections for live data updates and for reverse RPC calls (from the server to the client). Persevere supports two forms of Comet communication. The recommended communication is through REST Channels (also known as HTTP Channels). To use the REST Channels, you connect to the Channels servlet (/channels by default), providing a connection id with the Create-Client-Id header. The response will be of the content type "application/http" or "application/rest+json" (clients can use the Accept header to define which format is preferred). If the messages are in JSON format (application/rest+json), they will follow the form:

JSON Format PropertyDefinition
event This defines the method or verb that effectively modified data:
  • put - The resource has been modified, the new content should be included.
  • post - A new resource was added to the system, the content of the new resource should be included.
  • delete - The resource was deleted.
Note that the resource modification does not necessary have to be a result of an actual HTTP request with the given method, but the event indicates that the change was effectively the same as the result of the indicated HTTP method.
source On modification notification messages, this property refers to the resource location that was modified. This is a path, resolvable as a URL relative to the location of the requesting page.
data On modification notification messages, this property provides the new data for the modified object.
Messages sent from the server in application/http format will be sent as the content of the response in the form of HTTP response messages. Monitored/subscribed data that is modified will be sent as these messages. The inner HTTP message will include a "Content-Location" header which will identify the resource that was modified. The content of the inner HTTP message will be the new data for the modified resource. The HTTP message will also include a "Event" header that indicates what action took place (PUT, POST, or DELETE). When a post takes place, the only the posted data is included (not the resulting list/resource). Please see the HTTP Channels documentation for more information.

Persevere also supports Bayeux. When using Bayeux, subscription requests will be treated by resource subscriptions where the channel indicates the resource. Publishing messages through Bayeux to an object is functionally equivalent to executing the message method on an object. Messages can be published to resources and subscribers will receive the new messages.

SMD Support

You may get an Service Mapping Description (SMD) of the available data sources from the SMD servlet (which is /SMD by default). The SMD can be used for auto-configuration of services.

Cross-Site Data Access

JSONP

Persevere can provide access to the persisted data to other websites through JSONP. Authentication for cross-site requests is verified against the Referer header. If a user has authenticated against one website, another website will not be able to automatically use authentication to gain access to the user's data. This prevents cross-site request forgery (CSRF) attacks. Cross-site requests must be properly authenticated on their own in order to gain access to protected resources. To request an object from another site, include a "callback" parameter with the value indicating the name of the callback function to call with the data.

In many situations, it may be necessary to transmit and receive additional meta data which would otherwise be sent in HTTP headers. JSONP doesn't support headers. However, you can supply this header/metadata information using the JSONP metadata proposal.

XDomainRequest and Cross-Site XHR

Persevere also supports IE8's XDomainRequest and W3C Cross-Site Access Control. Requests can be made from other sites, and Persevere will apply the proper headers for use with these mechanisms. Persevere's security system still protects against CSRF with these modes of access.

Content Negotiation

Persevere supports content negotiation. Currently, Persevere supports JSON (pure JSON), JavaScript (basically JSON, but also allows functions). Other format handlers can be defined with custom MIME type handlers and alternate content types can also be uploaded as alternate MIME types for persisted objects.

Bulk Update

Object creation (POST) and object updates (PUT) can be done in bulk. Both POST and PUT requests may have an array of objects, and the request should be sent to table URL. POSTing an array of objects will result in the creation of multiple objects, and PUTing an array of objects will result in the update of multiple objects. When doing a bulk PUT, each object must have it's id property set to the appropriate id. For example:

PUT /MyTable/

[
 {"id":"2","foo":"bar"},
 {"id":"3","prop":44}
]