Published my first website in 1994
http://mail.bris.ac.uk/~cl3018
(Yes, it was hosted on a mail server...)
First used Allaire ColdFusion in 1999
Lead developer at Pixl8
One of the lead developers on Preside
Stand-alone websites
⇓
Data feeds over FTP
⇓
WSDL, SOAP XML etc.
⇓
REST APIs
SaaS applications
Web apps
Embedded widgets
Mobile apps
$ box install preside-ext-data-api
/**
*
*/
component {
property name="name" type="string" dbtype="varchar";
// more properties...
}
/**
* @dataApiEnabled true
*/
component {
property name="name" type="string" dbtype="varchar";
// more properties...
}
/api/data/v1/docs/html/
GET /entity/dog/
GET /entity/dog/{recordId}/
PUT /entity/dog/{recordId}/
POST /entity/dog/
The Preside Data API uses four verbs:
PATCH is not used
All updates use PUT
Only supplied fields are updated
Response body contains only the data
Metadata is passed in the headers
API is documented separately
/**
* @dataApiEnabled true
* @dataApiExcludeFields _version_is_draft, ↵
_version_has_drafts
* @dataApiUpsertExcludeFields _version_is_draft, ↵
_version_has_drafts,datemodified,datecreated
*/
component {}
/**
* @dataApiEnabled true
* @dataApiExcludeFields _version_is_draft, ↵
_version_has_drafts
* @dataApiUpsertExcludeFields _version_is_draft, ↵
_version_has_drafts,datemodified,datecreated
*/
component {}
/**
* @dataApiEnabled true
* @dataApiExcludeFields _version_is_draft, ↵
_version_has_drafts
* @dataApiUpsertExcludeFields _version_is_draft, ↵
_version_has_drafts,datemodified,datecreated
*/
component {}
/**
* @dataApiEnabled true
* @dataApiExcludeFields _version_is_draft, ↵
_version_has_drafts
* @dataApiUpsertExcludeFields _version_is_draft, ↵
_version_has_drafts,datemodified,datecreated
*/
component {}
/**
* @dataApiEnabled true
*/
component {
property name="name" type="string" dbtype="varchar";
// more properties...
}
/**
*/
component {
property name="name" type="string" dbtype="varchar";
// more properties...
}
/**
*/
component extends="app.base.api_entity" {
property name="name" type="string" dbtype="varchar";
// more properties...
}
/**
* @dataApiEntityName dogs
*/
component extends="app.base.api_entity" {
property name="name" type="string" dbtype="varchar";
// more properties...
}
/**
* @dataApiEntityName dogs
* @dataApiCategory core
*/
component extends="app.base.api_entity" {
property name="name" type="string" dbtype="varchar";
// more properties...
}
/**
* @dataManagerGroup dogs
* @dataApiCategory lookups
* @dataApiEntityName breeds
*/
component extends="app.base.api_entity" {}
/**
* @dataManagerGroup dogs
* @dataApiCategory lookups
* @dataApiEntityName breeds
* @dataApiVerbs GET,POST
*/
component extends="app.base.api_entity" {}
component {
property name="name" type="string" dbtype="varchar";
property name="main_photo"
relationship="many-to-one" relatedTo="asset";
property name="status"
relationship="many-to-one" relatedTo="homing_status";
property name="location"
relationship="many-to-one" relatedTo="location";
// more properties...
}
component {
property name="name" type="string" dbtype="varchar";
property name="main_photo"
relationship="many-to-one" relatedTo="asset"
dataApiAlias="photoUrl";
property name="status"
relationship="many-to-one" relatedTo="homing_status";
property name="location"
relationship="many-to-one" relatedTo="location";
// more properties...
}
component {
property name="name" type="string" dbtype="varchar";
property name="main_photo"
relationship="many-to-one" relatedTo="asset"
dataApiAlias="photoUrl"
dataApiDerivative="dogListingPhoto";
property name="status"
relationship="many-to-one" relatedTo="homing_status";
property name="location"
relationship="many-to-one" relatedTo="location";
// more properties...
}
component {
property name="name" type="string" dbtype="varchar";
property name="main_photo"
relationship="many-to-one" relatedTo="asset"
dataApiAlias="photoUrl"
dataApiDerivative="dogListingPhoto";
property name="status"
relationship="many-to-one" relatedTo="homing_status"
dataApiRenderer="dataApiHomingStatus";
property name="location"
relationship="many-to-one" relatedTo="location"
dataApiRenderer="dataApiLocation";
// more properties...
}
component {
private string function default( event, rc, prc, args={} ){
var statusId = args.data ?: "";
return renderLabel( "homing_status", statusId );
}
}
component {
private struct function default( event, rc, prc, args={} ){
var locationId = args.data ?: "";
return {
id = locationId
, label = renderLabel( "location", locationId )
};
}
}
api.title=Rescue Remedies API
api.description=This API provides REST access to the data for the Rescue Remedies dog rehoming website.
api.version=v1.0
api.favicon=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6CAYAAACI7Fo9AAAGVGlUWH...
# OBJECT LEVEL
entity.dogs.name=Dogs
entity.dogs.name.singular=Dog
entity.dogs.description=Here are all the dogs...
entity.dogs.sort.order=10
operation.dogs.get.description=Description for the paginated GET operation for your entity
# FIELD LEVEL
entity.dogs.field.location.description=ID and label of the location
settings.rest.apis[ "/data/v1" ].dataApiQueueEnabled = true;
settings.rest.apis[ "/data/v1" ].dataApiQueues = {
default = { pageSize=100, atomicChanges=true }
, lowpriority = { pageSize=10 , atomicChanges=false }
};
Users manually created in admin
Access token passed in as username
Potential for self-service by website users
Please use HTTPS!
WARNING!
Only use for a read-only API
settings.rest.apis[ "/data/v1" ].authProvider = "";
Public/private API
API versioning
Extension-specific API
Other separation of logic
settings.rest.apis[ "/public/v1" ] = {
authProvider = ""
, description = "Public REST API"
, dataApiNamespace = "public"
, dataApiQueueEnabled = false
};
settings.rest.apis[ "/public/v1/docs" ] = {
description = "Documentation for Public REST API"
, dataApiNamespace = "public"
, dataApiDocs = true
};
/**
* @labelField name
* @dataApiEnabled true
*/
component {
}
/**
* @labelField name
* @dataApiEnabled true
* @dataApiEnabled:public true
*/
component {
}
/**
* @labelField name
* @dataApiEnabled true
* @dataApiEnabled:public true
* @dataApiVerbs:public GET
*/
component {
}
/**
* @labelField name
* @dataApiEnabled true
* @dataApiEnabled:public true
* @dataApiVerbs:public GET
* @dataApiFields:public id,name,main_photo,status,location,breed,cross_breed,cross_with,gender,neutered,age,story
*/
component {
}
/**
* @labelField name
* @dataApiEnabled true
* @dataApiEnabled:public true
* @dataApiVerbs:public GET
* @dataApiFields:public id,name,main_photo,status,location,breed,cross_breed,cross_with,gender,neutered,age,story
* @dataApiFilterFields:public name,status,breed,gender
*/
component {
}
component {
property name="main_photo";
property name="status";
property name="location";
// more properties...
}
component {
property name="main_photo"
dataApiAlias:public="photo"
dataApiDerivative:public="dogListingPhoto";
property name="status";
property name="location";
// more properties...
}
component {
property name="main_photo"
dataApiAlias:public="photo"
dataApiDerivative:public="dogListingPhoto";
property name="status"
dataApiRenderer:public="dataApiHomingStatus"
dataApiType:public="string"
dataApiFormat:public="";
property name="location";
// more properties...
}
component {
property name="main_photo"
dataApiAlias:public="photo"
dataApiDerivative:public="dogListingPhoto";
property name="status"
dataApiRenderer:public="dataApiHomingStatus"
dataApiType:public="string"
dataApiFormat:public="";
property name="location"
dataApiRenderer:public="dataApiLocation"
dataApiType:public="string"
dataApiFormat:public="";
// more properties...
}
Custom authenticator
Custom endpoints
Interceptors
Uses your existing data model
Extensive configuration options
Auto-generated docs
Multiple APIs
Data change queues
Powerful customisations