As a developer we know it is important that you understand not only why our platform is superior, but also how. Below you’ll find links to some of the powerful features and functionality of our platform.
Click on the icons below for more details about each.
Everything is a scalable service, front to back.
Location
Every object can have an associated location. Powerful queries too.
Need to rent a place within 20 parsecs of Denver, Colorado? No problema.
//Number of meters in 20 parsecs
double distanceStraightUp = 20 * 3.08568025E+16;
NSString * queryString = [NSString stringWithFormat:@"/HousesForRent/(distance([%f, %f, %f], rentalLocation) lte %E)",
currLoc.coordinate.latitude, currLoc.coordinate.longitude, currLoc.altitude, distanceStraightUp];
[ff getArrayFromUri:queryString];
//Number of meters in 20 parsecs
double distanceStraightUp = 20 * 3.08568025E+16;
String queryString = String.format("/HousesForRent/(distance([%f, %f, %f], rentalLocation) lte %E)",
currLoc.getLatitude(), currLoc.getLongitude (), currLoc.getAltitude(), distanceStraightUp);
ffLocalhost.getArrayFromUri(queryString);
ff.getArrayFromUri("./ff/resources/HousesForRent/"
+ "(distance([39.7392, 104.9842, 1610], rentalLocation) lte 20 * 3.08568025E+16)",
function(response) {
//Manage the response
});
GET http://acme.fatfractal.com/GalacticRealEstate/ff/resources/HousesForRent/(distance([39.7392, 104.9842, 1610], rentalLocation) lte 20 * 3.08568025E+16)

Social
Integration with Facebook and Twitter APIs is de rigueur nowadays.
Your users can register and login to your app using their Facebook or Twitter credentials.
[ff loginWithFacebookID:stalker_ID andToken:@"Facebook access token"
onComplete:^(NSError *err, id ffUser, NSHTTPURLResponse *httpResponse) {
//Manage the login response
}];
ff.loginWithFacebookIDAndToken(stalker_ID, "Facebook access token");
Push
Push can be persnickety–but we make it as painless as possible.
When a new message is created in the AlienAttackMessage collection, let everyone know!
Create Handler in Configuration File
CREATE HANDLER RunLikeHell ON /AlienAttackMessage
CREATE AS javascript:require('scripts/InvasionHandlers').pushAlienAttackMessage();
Implement pushAlienAttackMessage in InvasionHandlers.js
var ff = require('ffef/FatFractal');
function pushAlienAttackMessage() {
var alienAttackMessage = ff.getEventHandlerData();
var userGuids = ff.getAllGuids("/FFUser");
ff.sendPushNotifications (userGuids, “FLEE! ” + alienAttackMessage.message);
}
exports.pushAlienAttackMessage = pushAlienAttackMessage;

Quickly and easily generate an email from the backend.
When an object is created on MindSpam, send an email saying, “cut it out!”
Register the event handler in your app’s config file
HANDLER NoMoreSpam ON /MindSpam CREATE AS javascript:require ('scripts/CeaseAndDesist').emailWarning();
Implement the emailWarning() function in CeaseAndDesist.js
var ff = require('ffef/FatFractal');
var newSpamObject = ff.getEventHandlerData();
ff.sendSMTPEmail({
host: 'smtp.gmail.com', port: 465,
toAddresses: [newSpamObject.emailAddressOfAuthor],
subject: 'Cease and Desist!',
body: "Mind spam will not be tolerated–you have been warned!"
});
Payment
Our backend payment service helps you integrate extraterrestrial (and more mundane) commerce into your app.
var payService = require ('ffef/PaymentService');
var collectionDemand = {
terminalId : "PLANET-X",
transactionId : '12345678',
cardNum : 'GALACTIC-1517-1232-1111',
cardExpMonth : 12,
cardExpYear : 4029,
totalAmount : 1000000000.00
}
var saleResponse = payService.processSaleRequest(collectionDemand);

Your SDKs should work for you—not force you to work for them.
Datastore
Get an instant, secure datastore and a customizable REST API.
No configuration, no boilerplate. Start persisting on your client and the schema creates itself.
NSData *fastAndFurryous; //mp4 video clip Character * roadRunner = [[Character alloc] initWithcharacterName:@"The Roadrunner" characterCreator:@"Chuck Jones" videoClip:fastAndFurryous]; [ff createObj:roadRunner atUri:@"/Characters"];
byte[] fastAndFurryous; //mp4 video clip
Character roadRunner = new Character("The Roadrunner", "Chuck Jones", fastAndFurryous);
ff.createObjAtUri(roadRunner, "/Characters"];
var roadRunner = {characterName:'The Roadrunner', characterCreator:'Chuck Jones', fastAndFurryous};
ff.createObjAtUri(roadRunner, "/Characters",
function(response) {/* handle success */},
function(xmlhttprequest) {/* handle error */}
);
POST http://acme.fatfractal.com/CartoonApp/ff/resources/Characters
Request body: {characterName:'The Roadrunner',characterCreator:'Chuck Jones'}
curl http://acme.fatfractal.com/CartoonApp/ff/resources/Characters/theRoadRunner/videoClip
--upload-file fastAndFurryous.mp4
--header "Content-Type: video/mp4"
Native SDKs
SDKs should be intuitive, powerful and elegant. They should feel natural.
- Persist & retrieve native models, as complex as you like.
- Port existing apps easily: no need to change your models.
- BLOBs & documents inline, in objects.
- No hash-maps or dictionaries required.
- Keep your object models pure.

Authorization, access and data security are baked in to the platform at all levels.
FFUser Object
Special user object with authentication built in.
All login requests are sent via SSL. The FFUser object is a normal object, but also has system fields, such as groups the user might belong to.
[ff loginWithUserName:@"Mark" andPassword:@"H00di3"];
ff.login ("Mark", "H00di3");
ff.login(userName, password);
FFUserGroup Object
Special group of FFUsers object with convenient methods and security associated. Users can belong to groups and groups can belong to users.
//Add an FFUserGroup to "mark" called "BFFs" FFUser *mark; FFUserGroup *BFFs; [mark addGroup:BFFs]; //Add FFUser "priscilla" to the "BFFs" FFUserGroup FFUserGroup *BFFs; FFUser *priscilla; [BFFs addUser:priscilla];
//Add an FFUserGroup to "mark" called "BFFs" FFUser mark; FFUserGroup BFFs; mark.addGroup(BFFs); //Add FFUser "priscilla" to the "BFFs" FFUserGroup FFUserGroup BFFs; FFUser priscilla; BFFs.addUser(priscilla);
//Add an FFUserGroup to "mark" called "BFFs" var mark, BFFs; ff.grabBagAdd(BFFs, mark, "groups"); //Add FFUser "priscilla" to the "BFFs" FFUserGroup FFUserGroupvar BFFs, priscilla; ff.grabBagAdd(priscilla, BFFs, "users");
Permissions
Assign permissions down to the object level, set declarative defaults, and change permissions at runtime.
Create /Photos collection and configure the default Object Access Policies
//By default, only the creator of a /Photo object can read and write it. All others can only read it. CREATE COLLECTION /Photos //Change the default permissions so that no one except the creator of the /Photo object can read it. PERMIT read:none write:none ON /Photos

Allow members of Mark's BFFs group to see the photos Mark added to /Photos
PERMIT read:creator.BFFs write:none ON /Photos

3
Add users to groups and her permissions changes
//Mark adds “sheryl_w” to his BFFs group through the app, allowing Sheryl to read his photos in /Photos. FFUserGroup *marks_bffs; FFUser *sheryl_w; [marks_bffs addUser:sheryl_w];
//Mark adds “sheryl_w” to his BFFs group through the app, allowing Sheryl to read his photos in /Photos. FFUserGroup marks_bffs; FFUser sheryl_w; marks_bffs.addUser(sheryl_w);
//Mark adds “sheryl_w” to his BFFs group through the app, allowing Sheryl to read his photos in /Photos. var marks_bffs, sheryl_w; ff.grabBagAdd(sheryl_w, marks_bffs, "users");
Create a new Object Access Policy on objects, at runtime, with a single line of code
//Mark changes a "party” pic so his BFFs group can’t read that /Photo object. Photo *party_pic; [ff setPermissionOnObject:party_pic readUsers:nil readGroups:nil writeUsers:nil writeGroups:nil];
//Mark changes a "party” pic so his BFFs group can’t read that /Photo object. Photo party_pic; ff.setPermissionOnObject(party_pic, null, null, null, null);
//Mark changes a "party” pic so his BFFs group can’t read that /Photo object. var party_pic; ff.setPermission(party_pic, [], [], [], []);

Delete user from an group and access changes immediately
FFUserGroup *BFFs; FFUser *tyler_w; [BFFs removeUser:tyler_w];
FFUserGroup BFFs; FFUser tyler_w; BFFs.removeUser(tyler_w);
var BFFs, tyler_w; ff.grabBagRemove(tyler_w, BFFs, "users");
Create a data model...get back a datagraph!
Reference
Objects can point to objects. Fetch references inline or
load on-demand for snappy performance.
Who is BART's (GUID 1234) father?
GET http://acme.fatfractal.com/simpsonsfans/ff/resource/Person/(firstName eq 'Bart')/father
//Retrieve homer directly Person * homer = [ff getObjFromUri:@"/Person/(firstName eq 'Bart')/father"]; //or, if bart has already been loaded on the client Person * homer = [bart father];
//Retrieve homer directly
Person homer = ff.getObjFromUri("/Person/(firstName eq 'Bart')/father");
//or, if bart has already been loaded on the client
Person homer = bart.father;
//Retrieve homer directly
var bart, homer;
ff.getObjFromUri("/Person/(firstName eq 'Bart')/father", function(response) {
homer = response;
});
//or, if bart has already been loaded on the client
homer = bart.father;
Grab Bag
Create many many-to-many relationships between objects without cluttering your object models. Scalable (NoSQL), works better than "joins" and effectively zero overhead.
Who are Bart's (GUID 1234) mom's sisters?
GET http://acme.fatfractal.com/simpsonsfans/ff/resource/Person/1234/mother/()/siblings/(gender eq 'Female')
//Retrieve grab bag of female siblings directly NSArray * bartsAunts = [ff getArrayFromUri:@"/Person/1234/mother/()/siblings/(gender eq 'Female')"]; //or, if bartsMom has already been loaded on the client NSArray * bartsAunts = [ff grabBagGetAllForObj:bartsMom grabBagName:@"siblings" withQuery:@"gender eq 'Female'"];
//Retrieve grab bag of female siblings directly
List bartsAunts = ff.getArrayFromUri("/Person/1234/mother/()/siblings/(gender eq 'Female')");
//or, if bartsMom has already been loaded on the client
List bartsAunts = ff.grabBagGetAllForQuery(bartsMom, "siblings", "gender eq 'Female'");
//Retrieve grab bag of female siblings directly
var bartsAunts;
ff.getObjFromUri("/Person/1234/mother/()/siblings/(gender eq 'Female')", function(response) {
bartsAunts = response;
});
//or, if bartsMom has already been loaded on the client
ff.grabBagGetAllForQuery(bartsMom, "siblings", "gender eq 'Female'", function(response)
{
bartsAunts = response;
});
Back Reference
System-generated for every object, separate from your object models. Traverse in reverse. Literally 50% less work.
Who's pointing at me (GUID 5678)? Doh!
//Get all objects that reference homer (pictured) GET http://acme.fatfractal.com/simpsonsfans/ff/resources/Person/5678/BackReferences" //Get all objects from the /Person collection which reference homer as siblings (herb) GET http://acme.fatfractal.com/simpsonsfans/ff/resources/Person/5678/BackReferences.Person.siblings //Get all objects from the /Person collection which reference homer as father (bart, lisa, maggie) GET http://acme.fatfractal.com/simpsonsfans/ff/resources/Person/5678/BackReferences.Person.father
//Get all objects that reference homer (pictured) NSArray * referringToHomer = [ff grabBagGetAllForObj:homer grabBagName:@"BackReferences"]; //Get all objects from the /Person collection which reference homer as siblings (herb) NSArray * peopleWhoReferToHomerAsSibling = [ff grabBagGetAllForObj:homer grabBagName:@"BackReferences.Person.siblings]; //Get all objects from the /Person collection which reference homer as father (bart, lisa, maggie) NSArray * homersChildren = [ff grabBagGetAllForObj:homer grabBagName:@"BackReferences.Person.father];
//Get all objects that reference homer (pictured) List referringToHomer = ff.grabBagGetAll(homer, "BackReferences"); //Get all objects from the /Person collection which reference homer as siblings (herb) List peopleWhoReferToHomerAsSibling = ff.grabBagGetAll(homer, "BackReferences.Person.siblings"); //Get all objects from the /Person collection which reference homer as father (bart, lisa, maggie) List homersChildren = ff.grabBagGetAll(homer, "BackReferences.Person.father");
//Get all objects that reference homer (pictured)
ff.grabBagGetAll(homer, "BackReferences", function(response) {
});
//Get all objects from the /Person collection which reference homer as siblings (herb)
ff.grabBagGetAll(homer, "BackReferences.Person.siblings", function(response) {
});
//Get all objects from the /Person collection which reference homer as father (bart, lisa, maggie)
ff.grabBagGetAll(homer, "BackReferences.Person.father", function(response) {
});
Queries
Complex queries deciphered on the backend. No limits on nesting. Permissions respected, as always.
Every father
/Person/()/father
Every mother
/Person/()/mother
Bart's grandfathers
/Person/(firstName eq 'Bart')/father or mother/()/father
All of Bart's grandparents
/Person/(firstName eq 'Bart')/father or mother/()/mother or father
Ling's cousins
/Person/(firstName eq 'Ling')/father or mother/()/siblings/()/BackReferences.Person.mother or BackReferences.Person.father
Code your app's backend with our serverside sdks.
Javascript Serverside SDK
Business logic, event handlers, extending your API...use JavaScript!
- CommonJS standard
- Same API to your app's backend as the clientside
- Simple to link events to code
FatFractal Description Language (FFDL, "fiddle")
Configure application default settings with a simple syntax.
Set defaults for things like...
- Learn Mode: Your app's backend learns your data model as you code the client.
- Permissions: Set any number of default Object Access Policies as your app starts up.
- Lock down: Schema up objecttypes and collections for validation, if you want.
- Events & Extensions: Connect events and API extensions to code.
Configure your app, set permissions, link triggers to code, and more, all in application.ffdl.
ffef getFFDL http://acme.fatfractal.com/myApplication thePassword

Event handlers leverage your backend for security, reliability and power.
Handling Events
CRUD EVENT
- Trap on create, update or delete object
- Define handlers in a simple config file
- Code handlers in Javascript serverside
- Cascading handlers, sync and async
HANDLERS FLOW
- PRE fires before a CRUD event, sync
- POST fires after a CRUD event, sync
- ASYNC fires after a CRUD event, async
- Async is default
CREATE HANDLER ValidateUser ON /FFUser CREATE AS javascript:var h = require ('scripts/myHandlers'); h.validateUser();
function validateUser() {
var activateRequest = ff.createObjAtUrl({createdBy:'system',userGuid:ff.getEventHandlerData().guid}, "/ActivationRequest");
ff.sendSMTPEmail("I_Just_Registered@gmail.com", "465", "true", "465", "registration@ff.com", "thePassword", "",
ff.getEventHandlerData().email, "Activate your account",
"To activate your account, please click on this link:" + ff.getHttpAppAddress() + "/ff/ext/verifyRegistration?guid=" + activateRequest.guid);
}
Execute custom code on the backend as a resource to your app.
CREATE EXTENSION /verifyRegistration AS javascript:var f = require ('scripts/myExtensions'); f.verifyRegistration (REQUEST);
function verifyRegistration() {
var guid = ff.getEventHandlerData().httpParameters['guid'];
var user = ff.getUser(ff.getObjFromUrl("/ff/resources/ActivationRequest/" + guid, "users").userGuid);
user.active = true;
ff.updateObj(user);
ff.deleteObj(activationRequest);
var r = ff.response();
r.result = 'Your account has been activated';
r.statusMessage = "User now activated";
r.mimeType = "text/html";
}

Languages
The FatFractal Engine will support additional serverside, language modules; we are adding more all the time.









Facebook
Linked In
Twitter