Model Services - REST
In this lesson, you will learn about Model Services. This is the automatic REST interface to the business model. First read about the details of how to make requests to Model Services, then we will do some activities.
Model Services Syntax
Documentation for the JSON version of the REST interface may be accessed at <serverRoot>/openAPI.html. It also gives you the opportunity to try it out. For example: http://localhost:7080/training/openAPI.html.
Model Services provide API access to the Business Model for REST style create, read, update, delete, and invoke. They are available at <serverRoot>/<format>/<objectType>/<id>?<parameters>
along with an HTTP Method to indicate the CRUD (create, read, update, delete) verb.
If you don't specify an "attributes" parameter, all public primitive attributes are returned.
If you do specify a list of primitive attributes in the attributes parameter, then only those attributes will be returned.
e.g. (firstName lastName)
Associated objects can be retrieved using nested lists in the attributes parameter. Simply specify a non-primitive attribute name then a list of one or more attributes. For example:
firstName lastName (addrs city state zip))
These nestings can continue. For example:
(firstName lastName (addrs city state zip (type name)))
Attributes can also be retrieved polymorphically - meaning attributes that only exist on subclasses can be retrieved when reading the base class. (see examples below)
Attribute names in the where and order by parameters may take the simple form <attributeName>
or alternatively (@ <attributeName>)
. The second form is recommended. In this case, the "@
" represents the current instance and may be used to traverse arbitrarily long association paths. For example, on User we may specify:
(= (@ person homeAddress city) "Toronto")
[{ "$class": "Person", "$oid": "10031C7A7EF46D45ADBC72090BCCD04825", "lastName": "Cotherman", "addrs": [ { "$class": "Address", "$oid": "10EE388953279847C6B7DB63CACAB774C0", "state": null, "zip": null, "type": { "$class": "AddressType", "$oid": "1000000000000010008000CEEDED004000", "name": "Business" }, "city": null } ], "firstName": "Joseph", "initials": null } ]
$class and $oid
The $class
and $oid
attributes of an object are always returned.
They are the class name of the object and unique identifier respectively.
Request Structure
Model Services are available at <serverRoot>/<format>/<objectType>/<id>?<parameters>
along with an HTTP Method to indicate the CRUD verb.
serverRoot
The host and context root of the server. For example: http://localhost:7080/training.
This is defined in your environment file.
format
Defines the return type for the request. Formats supported include "json", "xml", and "web" for a SOAP encoding. These endpoints support the REST style requests discussed here. We use the json format for all of the examples in this document. To see the effect of another protocol, simply replace json with xml or web in the examples. Additionally, there are other supported protocols including soap and text but they use a different API style called Generic RPC and are not discussed here.
objectType
Specifies the object type to work on e.g. Person, Household, or Account
id
An optional unique identifier for the item to work on. If there is no id in the URL, the request will work on a collection, if there is one, it will work on a specific instance. For example: http://localhost:7080/training/json/Person/101DBCCB4F0B4E4345BDD1C68ECE20DBBD?$indent.
parameters
The parameters, attributes, where
, orderby
, count
and offset,
employ the same syntax as with any object query (in other words, the same syntax used in scripting for reads).
$indent
Pretty print the output.
We include the $indent
parameter in many of the following examples. It is only for readability and is not required.
attributes
The list of attributes, or associated objects, to proactively retrieve. To retrieve all primitive attributes, leave this parameter out. To retrieve selected primitive attributes or just the id of associated objects, list them in the brackets. For example: http://localhost:7080/training/json/Person?attributes=(firstName lastName initials addrs)&$indent.
To traverse association paths you can wrap associated objects in brackets then list their primitive and associated attributes. This can be done recursively as seen here. For example: http://localhost:7080/training/json/Person?attributes=(firstName lastName initials (addrs city state zip (type name)))&$indent.
{ "$class": "Person", "$oid": "10031C7A7EF46D45ADBC72090BCCD04825", "lastName": "Cotherman", "addrs": [ { "$class": "Address", "$oid": "10EE388953279847C6B7DB63CACAB774C0", "state": null, "zip": null, "type": { "$class": "AddressType", "$oid": "1000000000000010008000CEEDED004000", "name": "Business" }, "city": null } ], "firstName": "Joseph", "initials": null },
Two special (read advanced ) syntax exist for downcast and annotations.
Attribute downcast syntax (@@) - Polymorphic read is used to proactively load subclass specific attributes when reading from a base class. In the following example we only retrieve the homePhone attribute for Entity records that are of type Person, otherwise we don't include it.
http://localhost:7080/training/json/Entity?attributes=(fullName lastName (@@ Person firstName homePhone))&$indent
Annotations syntax - create calculated attributes on-the-fly
In this example, return the lastName attribute, a calculated _lnLen attribute which is the length of the last name, and a _fullName calculated attribute. We use the underscore here just to avoid name collisions. For example:
http://localhost:7080/training/json/Person?attributes=(lastName (: _lnLen (string-length (@ lastName))) (: _fullName (string-append (@ lastName) (@ firstName))))&$indent
Aggregate functions - can be applied to subcollections on instances. Supported operators include count
, average
, min
, max
, sum
. For example:
http://localhost:7080/training/json/Person?attributes=(firstName lastName (: _countryCnt (count (@ addrs country))) (: _uniqueCountryCnt (count (unique (@ addrs country)))) (: _nullCodeCnt (count (null? (@ addrs zip)))) (: _avg (average (string-length (@ addrs city)))) (: _min (minimum (string-length (@ addrs city)))) (: _max (maximum (@ addrs city))) (: _sum (sum (string-length (@ addrs city)))))&$indent
where
The where clause. The following examples illustrate the use of associations, reverse associations and qualified associations.
Simple where not using the @ syntax - (and ( = firstName "Tim") (= lastName "Lamont"))
Simple where using the @ syntax - (and ( = (@ firstName) "Joe") (= (@ lastName) "Test"))
Any operator - Reduces the multiplicity of an association to 1
Expressing queries that are impossible to do only with joins. In this example, return people who have at least one address in Toronto and at least one in Richmond Hill. Nothing will be returned by the equals operator equivalent (and (= (@ addrs city) "Toronto") (= (@ addrs city) "Richmond Hill"))
(and (any (= (@ addrs city) "Toronto")) (any (= (@ addrs city) "Richmond Hill"))) ; two part association path
Optimizing database filtering on collection associations. In this example, the database will return only one person record per matching addresses vs returning as many copies of a person record as there are matching addresses when using the equals operator equivalent (Person'read ... '(= (@ addrs city) "Toronto") ...)
(any (= (@ addrs city) "Toronto") ; two part association path
Conditions in where clause association paths. This syntax allows us to restrict parts of association paths to specific subclasses and also to apply arbitrary conditions on association paths.
The most common use case: limit an association based on a qualifier e.g. look for all Entities with a user named "Shaw" playing their advisor role
(= (@ coverage (= (@ coverageRole roleName) "Advisor") userPerson lastName) "Shaw")
Subclass restriction example: note that homeAddress is an attribute that is only found on the Person subclass of Entity
(= (@ entity (instance? (@) Person) homeAddress city) "Toronto")
Arbitrary condition example: reads all instances of the Telcom class that has an entity with a lastname of "Shaw" that has an address in "Toronto" with and address2 line that is not null.
(not (null? (@ entity (= (@ lastName) "Shaw") addrs (= (@ city) "Toronto") address2)))
Note
Conditions may be applied in reverse associations as well as forward associations
Reverse association syntax (@@). Used to query from the end of an association "back" to the class you are reading. Forward associations take the form (@ part1 part2 part3 ...). The @ represents an instance of the class being read. Revese associations take the form (@@ <ClassName> part3 part2 part1) where part1 represents an attribute with a type of the class being read. You can usually just navigate the forward association, but you may see this syntax if you are tracing requests from NexJ UI clients.
Address?where=(= (@@ User person addrs) <userId>)
orderBy
The expressions on which to order the collection - a list of (<sort-expr> . <ascending?>) pairs.
E.g. (((@ firstName) . #t) ((@ lastName) . #f)) or more simply (since it is a single associate path length) ((firstName . #t) (lastName . #f))
count
The number of instances to retrieve. Null '() for the default value of -1, meaning up to the readLimit specified in the data source. One cannot pass in a larger count than readLimit defined in the data source.
offset
The number of top level instances to skip from the beginning of the retrieved collection. Default value of 0.
HTTP Method
Maps as follows. GET=read, POST=create or invoke, PUT=update, DELETE=delete
Body
The body of the request when creating, updating, or optionally deleting is an object structure similar to that returned during a read (or GET).
Try it out
Let's start by looking at the Open API documentation for Model Services. Navigate to http://localhost:7080/training/openAPI.html. You should get a response something like...
If we expand the GET /Person section, we will see that get supports the following querystring parameters.
At the top of the parameters section, press the button.
In the attributes textbox, enter (firstName lastName (addrs city state zip)) and in the count textbox enter 3.
Then press the blue Execute button at the bottom of the parameters section. That will execute the request against the server and return the result in the Response body section.
The request URL is http://localhost:7080/training/json/Person?attributes=(firstName%20lastName%20(addrs%20city%20state%20zip))&count=3.
Try out some of the other get parameters in your browser i.e. attributes, where, orderBy, count, and offset. You will also want to put &$indent in the URL as well so your output is more readable.
You can try out some of the other methods (POST, DELETE, PUT) by installing Postman and trying it out.