RESTForms - REST API for your classes
In this article I would like to present the RESTForms project - generic REST API backend for modern web applications.
The idea behind the project is simple -after I wrote several REST APIs I realized that generally, REST API consists of two parts:
- Work with persistent classes
- Custom business logic
And, while you'll have to write your own custom business logic, RESTForms provides all things related to working with persistent classes right out of the box.
Use cases
- You already have a data model in Caché and you want to expose some (or all) of the information in a form of REST API
- You are developing a new Caché application and you want to provide a REST API
Client side
This project is developed as a web applications backend, so JS just gets it. No format conversion required.
Note: CRUD
4 operations can be done over an object or a collection:
- Create
- Read
- Update
- Delete
Features
What can you already do with RESTForms:
- CRUD over exposed class - you can get class metadata, create, update and delete class properties
- CRUD over object - you can get, create, update and delete objects
- R over object collections (via SQL) - protected from SQL injections
- Self-discovery – first you get a list of available classes, after that you can get class metadata, and based on that metadata you can do CRUD over object
Paths
Here's the table of the main paths, showcasing what can you do via RESTForms.
URL | Description |
info | List of all available classes |
info/all | Get metadata for all classes |
info/:class | Class metadata |
field/:class | Add property to class |
field/:class | Modify class property |
field/:class/:property | Delete class property |
object/:class/:id | Retrieve object |
object/:class/:id/:property | Retrieve one property of the object |
object/:class | Create object |
object/:class/:id | Update object from dynamic object |
object/:class | Update object from object |
object/:class/:id | Delete object |
objects/:class/:query | (SQL) Get objects for the class by query |
objects/:class/custom/:query | (SQL) Get objects for the class by custom query |
How do I start using RESTForms?
- Import the project from GitHub (recommended method: add as a submodule to your own repo, or just download a release)
- For each class, you wish to expose via RESTForms
- Inherit from adaptor class
- Specify permissions (for example you may expose some classes as read-only)
- Specify property used as a display value for an object
- Specify display names for properties, you wish to display
Setup
- Download and import from latest release on release page 20161.xml (for Caché 2016.1) or 201162.xml (for Caché 2016.2+) into any namespace
- Create new web application /forms with Dispatch class Form.REST.Main
- Open http://localhost:57772/forms/test?Debug in the browser to validate install (should output {"Status": "OK"} and possibly prompt for password).
- If you want test data, call:
do ##class(Form.Util.Init).populateTestForms()
Example
First, you need to know what classes are available. To get that information, call:
http://localhost:57772/forms/form/info
You'll receive something like this as a response:
[ { "name":"Company", "class":"Form.Test.Company" }, { "name":"Person", "class":"Form.Test.Person" }, { "name":"Simple form", "class":"Form.Test.Simple" } ]
There are currently 3 sample classes (provided with RESTForms), let's see metadata for Person (Form.Test.Person class). To get that information, call:
http://localhost:57772/forms/form/info/Form.Test.Person
In response you'll receive class metadata:
{ "name":"Person", "class":"Form.Test.Person", "displayProperty":"name", "objpermissions":"CRUD", "fields":[ { "name":"name", "type":"%Library.String", "collection":"", "displayName":"Name", "required":0, "category":"datatype" }, { "name":"dob", "type":"%Library.Date", "collection":"", "displayName":"Date of Birth", "required":0, "category":"datatype" }, { "name":"ts", "type":"%Library.TimeStamp", "collection":"", "displayName":"Timestamp", "required":0, "category":"datatype" }, { "name":"num", "type":"%Library.Numeric", "collection":"", "displayName":"Number", "required":0, "category":"datatype" }, { "name":"аge", "type":"%Library.Integer", "collection":"", "displayName":"Age", "required":0, "category":"datatype" }, { "name":"relative", "type":"Form.Test.Person", "collection":"", "displayName":"Relative", "required":0, "category":"form" }, { "name":"Home", "type":"Form.Test.Address", "collection":"", "displayName":"House", "required":0, "category":"serial" }, { "name":"company", "type":"Form.Test.Company", "collection":"", "displayName":"Company", "required":0, "category":"form" } ] }
What does all that mean?
Class metadata:
- name - display name for the class
- class - underlying persistent class
- displayProperty - object property to use, when displaying object
- objpermissions - what can a user do with an object. In current case, user can create new objects, modify existing ones, delete existing objects and get the
Property metadata:
- name - property name - same as in the class definition
- type - property class
- collection - is list/array collection
- displayName - display property name
- required - is this property required
- category - property type class category. Follows usual Caché class categories, except all RESTForms enabled classes are shown as "form"
In class definition it looks like this:
/// Test form: Person
Class Form.Test.Person Extends (%Persistent, Form.Adaptor, %Populate)
{
/// Form name, not a global key so it can be anything
/// Set to empty string (like here) to not have a class as a form
Parameter FORMNAME = "Person";
/// Default permissions
/// Objects of this form can be Created, Read, Updated and Deleted
/// Redefine this parameter to change permissions for everyone
/// Redefine checkPermission method (see Form.Security) for this class
/// to add custom security based on user/roles/etc.
Parameter OBJPERMISSIONS As %String = "CRUD";
/// Property used for basic information about the object
/// By default getObjectDisplayName method gets its value from it
Parameter DISPLAYPROPERTY As %String = "name";
/// Use value of this parameter in SQL, as ORDER BY clause value
Parameter FORMORDERBY As %String = "dob";
/// Person's name.
Property name As %String(COLLATION = "TRUNCATE(250)", DISPLAYNAME = "Name", MAXLEN = 2000);
/// Person's Date of Birth.
Property dob As %Date(DISPLAYNAME = "Date of Birth", POPSPEC = "Date()");
Property ts As %TimeStamp(DISPLAYNAME = "Timestamp") [ InitialExpression = {$ZDATETIME($ZTIMESTAMP, 3, 1, 3)} ];
Property num As %Numeric(DISPLAYNAME = "Number") [ InitialExpression = "2.15" ];
/// Person's age.<br>
/// This is a calculated field whose value is derived from <property>DOB</property>.
Property аge As %Integer(DISPLAYNAME = "Age") [ Calculated, SqlComputeCode = { set {*}=##class(Form.Test.Person).currentAge({dob})}, SqlComputed, SqlComputeOnChange = dob ];
/// This class method calculates a current age given a date of birth <var>date</var>.
ClassMethod currentAge(date As %Date = "") As %Integer [ CodeMode = expression ]
{
$Select(date="":"",1:($ZD($H,8)-$ZD(date,8)\10000))
}
/// Person's spouse.
/// This is a reference to another persistent object.
Property relative As Form.Test.Person(DISPLAYNAME = "Relative");
/// Person's home address. This uses an embedded object.
Property Home As Form.Test.Address(DISPLAYNAME = "House");
/// The company this person works for.
Relationship company As Form.Test.Company(DISPLAYNAME = "Company") [ Cardinality = one, Inverse = employees ];
}
RESTForms enabling a class
So, to make this class RESTForms enabled, I started with the usual persistent class and:
- Extended it from Form.Adaptor
- Added FORMNAME parameter with the value - name of the class
- Added OBJPERMISSIONS parameter - CRUD for all permissions
- Added DISPLAYPROPERTY parameter - property name used to display object name
- Added FORMORDERBY parameter - default property to sort by for queries using RESTForms
- For each property I want to see in metadata I added DISPLAYNAME property parameter
That's all. After compilation, you can use the class with RESTForms.
As we generated some test data (see Installation, step 4), let's get Person with id 1. To get the object call:
http://localhost:57772/forms/form/object/Form.Test.Person/1
And, here's the response (generated data, may differ):
{ "_class":"Form.Test.Person", "_id":1, "name":"Klingman,Rhonda H.", "dob":"1996-10-18", "ts":"2016-09-20T10:51:31.375Z", "num":2.15, "аge":20, "relative":null, "Home":{ "_class":"Form.Test.Address", "House":430, "Street":"5337 Second Place", "City":"Jackson" }, "company":{ "_class":"Form.Test.Company", "_id":60, "name":"XenaSys.com", "employees":[ null ] } }
To modify the object (specifically, num property), call:
PUT http://localhost:57772/forms/form/object/Form.Test.Person
With this body:
{ "_class":"Form.Test.Person", "_id":1, "num":3.15 }
Note that for better speed, only _class, _id and modified properties should be in the request body.
Now, let's create a new object. Call:
POST http://localhost:57772/forms/form/object/Form.Test.Person
With this body:
{ "_class":"Form.Test.Person", "name":"Test person", "dob":"2000-01-18", "ts":"2016-09-20T10:51:31.375Z", "num":2.15, "company":{ "_class":"Form.Test.Company", "_id":1 } }
If the object creation was successful, RESTForms would return an id:
{"Id": "101"}
Otherwise, an error would be returned in JSON format. Note that all persistent object properties should be referenced only by _class and _id properties.
And finally, let's delete our new object. Call:
DELETE http://localhost:57772/forms/form/object/Form.Test.Person/101
That's full CRUD over Form.Test.Person class.
Demo
You can try RESTForms online here (user: Demo, pass: Demo) .
Additionally there is a RESTFormsUI application - editor for RESTForms data, check it out here (user: Demo, pass: Demo). Screenshot of the class list:
Conclusion
RESTForms does allmost of the work, required from the REST API as far as persistent classes are concerned.
What's next
In this article, I just started talking about RESTForms features. In the next article, I'd like to tell you about some advanced features - queries, that allow client to get slices of data safely, with no risk of SQL injections. Read about queries in the second part of the article.
There is also a RESTFormsUI - editor for RESTForms data.
Links