NoServer Features

As developers, 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;

Email

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.

Console & Data Editor

Administrate your apps and domains and inspect or change app data.

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.

1
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
2
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");
4
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, [], [], [], []);
5
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!

CREATE OBJECTTYPE Person (firstName STRING, lastName STRING, gender STRING, mother REFERENCE, father REFERENCE, siblings GRABBAG)

1

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;
2

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;
});

3

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) {
});
4

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

SCHEDULED

  • SCHEDULE name; time;
    AS javascript:jsCode
  • Reports, alerts, batch
  • Data maintenance

Easy to set up

Event handlers offload the work to the backend for power and efficiency.

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.


Custom Code

Extend your API with custom code

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.

Get Started!