How to write a driver

Start by copying an existing driver, closest to the underlying datastore. You can find the list of existing drivers here. The driver is called by the DataHandler and must provide a method for the main functionality it implements. 
Let’s walk through each one – the driver you are writing – lets call it foo.

The constructor

The returns an instance of DriverFoo.

var DriverFoo = function (handler,tableName,id,optOb)  {
  var siloId = tableName;
  var self = this;
  var parentHandler = handler;
  var enums = parentHandler.getEnums(); 
...
...
...
    return self;
}
argument type purpose
 handler DataHandler  The DataHandler object that calls the driver
 tableName stringEquivalent to a table, a parse.com class or some other unique way of segregating data handled by this driver instance. This is known as the siloId
 id string An optional string that uniquely identifies the set of tables. This is roughly equivalent to a database name or a set of tables.
 optOb object An object that may be used in your driver, such as a datbase object

Mandatory methods

name returns purpose
 getDriveHandle objectreturns the handle used by the driver. A handle is the object the driver uses to access the native API and may have been passed by the DataHandler in optOb, or constructed in the Driver – for example a sheet object
getTableName string returns the siloId
remove(queryOb, queryParams) resultObject remove data that match the query object and query parameters
save(data) resultObject saves the array of objects in data
query(queryOb,queryParams, optKeepIds) resultObject returns data that match the query object and query parameters. Sometimes databases add fields like created date and so on. If optKeepIds is false, these must be first be removed from the returned data
count(queryOb,queryParams) resultObject returns the count of objects that match the queryOb and queryParams. 
 getVersion string returns the handler name and version

resultObject

Each method should return a resultObject. This is created by a method of the DataHandler, and is called like this. 
parentHandler.makeResults (handleCode,handleError,resultArray,driverIds,handleKeys);

name type purpose
 handleCode enums.CODE the success/error code from the available choices in the data handler ENUM
 handleError string The error message associated with enums.CODE is generated automatically. Anything here is supplemental
 resultArray array of objects The results from the driver action
 driverIds array of objects if keepIds is true in a query, you need to return an array of any additional ids and other control data that the database api adds to the record (which you will have stripped out of the resultArray)
 handleKeys array of keys  if keepIds is true in a query, you need to return an array of key values that you could later use to identify a specific object

queryParams

This is an object like {sort:'country',limit:20,skip:100}. You should use parentHandler.getQueryParams (queryParams) to return an array of validated objects from your param object, sorted in the correct order (for example sort comes before limit). it Will return a resultObject. If resultObject.handleCode is enums.CODE.OK then all the parameters were valid. You can access and execute them through.

resultObject.data.forEach (function (p) {

    //p.param - the name
    //p.value - the supplied value
    //depending on the parameter  these are also available
    //p.sortKey
    //p.sortDescending 
    //p.limit
    //p.skip

});

At a minimum your driver should implement skip, limit and sort. Other parameters are passed through as provided. If you see one you havent implemented, you should error with enums.CODE.PARAMNOTIMPLEMENTED

Useful functions 

These are available from the DataHandler and can be called from the driver.

name returns purpose
parentHandler .makeResults (handleCode, handleError,resultArray) resultObject should be called at the end of your function to construct a standard resultObject format
parentHandler.getQueryParams (queryParams) resultObject used to validate the values given for queryParams and sort then in the correct order (for example so that sort comes before limit). resultObject.data will contain an array of objects that are the parameters and values to use to modify the query search results.
parentHandler.generateUniqueString() string you may need a unique string. This will generate one.
parentHandler.getEnums();  object gets the constants defined for all drivers.
 parentHandler.rateLimitExpBackoff ( callBack, sleepFor , maxAttempts, attempts ) whatever your callback returns Does an automatic exponential back off if you callback() fails for some rate limited error. For more details see Backing off on rate limiting

There are also data handler functions for object flattening and constraints processing as described in Dealing with constraints

Error handling

All errors are defined in enums.CODE and these codes should be passed as handleCode to parentHandler.makeResults along with any additional comments in handleError.

A typical error might be

catch(err) {
      handleError = err;
      handleCode =  enums.CODE.DRIVER;
    }

Constants for error handling.

The can be accessed through parentHandler.getEnums(); 

var dhConstants = { 
  DB: {
    SCRIPTDB:1,
    PARSE:2,
    SHEET:3,
    FUSION:4,
    ORCHESTRATE:5,
    DRIVE:6,
    IMPORTIO:7,
    PROPERTIES:8,
    DATASTORE:9,
    MEMORY:10,
    MONGOLAB:11,
    MYSQL:12,
    FIREBASE:13,
    SCRATCH:14
  },
  LOCKING : {
    AGGRESSIVE:1,
    ENABLED:2,
    DISABLED:3
  },
  TRANSACTIONS : {
    ENABLED:2,
    DISABLED:3
  },
  SETTINGS: {                 
    CACHEEXPIRY:60*5, // 5 minutes
    LOCKEXPIRY:(1+Math.pow(2,5+1))*2000, // till expiry of backoff attempts
    ULENGTH:6,
    KEEPIDS:false,
    VERSION:getLibraryInfo().info.name+':'+getLibraryInfo().info.version,
    MAXATTEMPTS:5,
    SLEEPFOR:2000,
    MAXPROPERTYNAME:100,
    UAKEY:'uaKey',
    CONSTRAINT:'__CONSTR$KEY$',
    NOSILO:'__NO$SILO$',
    EXTRAROWS:30,
    PROTECTEXPIRY:3*60*1000,
    MAX_NUMBER: 9007199254740992, // this stuff is for lucene query ranges, dont know how to do negative numbers.
    MIN_NUMBER: 0,  
    MAX_STRING: '\uffff',
    MIN_STRING: '\0000',
    HIDDEN_KEY:'_$dbab$key$_'
  },
  ACTIONS: ['query','save','remove','count','get','update'],
  CODE: {  // negative errors are bad, postive are warning, 0 is good
    OK:0,
    CACHE:1,
    NULL_DATA:2,
    PUBLISH:3,
    ROLLBACK:4,
    TRANSACTION_INCAPABLE:5,
    DRIVER:-1,
    LOCK:-2,
    RESULT:-3,
    PROPERTY:-4,
    HTTP:-5,
    NOMATCH:2,
    OPERATION_NOT_ALLOWED:-6,
    ASSERT:-7,
    PARAMNOTIMPLEMENTED:-8,
    UNKNOWN_DRIVER:-9,
    HANDLE_GET:-10,
    REST_GET:-11,
    REST_ERROR:-12,
    NO_ACTION:-13,
    PARAMS_MISSING:-14,
    ACTION_FAILED:-15,
    UNDER_CONSTRUCTION:-16,
    OPERATION_UNSUPPORTED:-17,
    CLIENT_ERROR:-18,
    KEYS_AND_OBJECTS:-19,
    CHECKSUM:-20,
    TRANSACTION_ACTIVE:-21,
    TRANSACTION_ROLLBACK:-22,
    TRANSACTION_ROLLBACK_FAILED:-23,
    TRANSACTION_FAILURE:-24,
    TRANSACTION_ID_MISMATCH:-25,
    DRIVER_ASSERTION:-26,
    KEY_ASSERTION:-27
  },
  ERROR: {
    OK:'',
    CACHE:'this came from cache',
    DRIVER:'driver returned an error',
    LOCK:'unable to get a lock',
    RESULT:'error in driver results',
    PROPERTY:'invalid property/key',
    NOMATCH:'nothing matched the query',
    ASSERT:'synch or programming error',
    PARAMNOTIMPLEMENTED:'this driver has not implemented this parameter yet',
    HTTP:'some connection problem',
    OPERATION_NOT_ALLOWED:'policy disallows operation',
    UNKNOWN_DRIVER:'unknown driver type',
    HANDLE_GET:'could not get a datahandler',
    REST_GET:'could not get a restahandler',
    REST_ERROR:'some problem with the parameters',
    NO_ACTION:'no action has been performed yet',
    PARAMS_MISSING:'some parameters are missing',
    PUBLISH:'nothing to publish',
    ACTION_FAILED:'action not known or invalid',
    NULL_DATA:'null data was ignored',
    UNDER_CONSTRUCTION:'method still under construction for this driver - check back later',
    OPERATION_UNSUPPORTED:'this driver does not support method',
    CLIENT_ERROR:'reserved for client error message',
    KEYS_AND_OBJECTS:'number of keys do not match number of objects',
    CHECKSUM:'data has changed since getting keys',
    TRANSACTION_ACTIVE:'transaction already active',
    ROLLBACK:'operation(s) rolled back',
    TRANSACTION_ROLLBACK:'transaction failed, but rolled back successfully',
    TRANSACTION_ROLLBACK_FAILED:'transaction failed, and roll back failed',
    TRANSACTION_FAILURE:'some transaction failure',
    TRANSACTION_INCAPABLE:'transactions not enabled for this driver',
    TRANSACTION_ID_MISMATCH:'trying to end the wrong transaction',
    DRIVER_ASSERTION:'driver assertion failure',
    KEY_ASSERTION:"trying to save an object which already has a key"
  },
  CONSTRAINTS: {
    LT:'$lt',
    GTE:'$gte', 
    GT:'$gt',
    NE:'$ne',
    IN:'$in',
    NIN:'$nin',
    EQ:'$eq',
    LTE:'$lte'
  },
  FUSION_CONSTRAINTS: {
    LT:'<',
    GTE:'>=', 
    GT:'>',
    NE:'NOT EQUAL TO',
    IN:'IN',
    EQ:'=',
    LTE:'<='
  },
  DATASTORE_CONSTRAINTS: {
    LT:'LESS_THAN',
    GTE:'GREATER_THAN_OR_EQUAL', 
    GT:'GREATER_THAN',
    LTE:"LESS_THAN_OR_EQUAL",
    EQ:"EQUAL"
  }
};

var ENUMS = dhConstants;