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  string Equivalent 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  object returns 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;

For help and more information join our forum, follow the blog or follow me on Twitter