Usage Tutorial

Auto-Load Models from a Directory

Define a Model named PersonModel.js in models Directory


export default {
    fields:{
        name    : "text",
        surname : "text",
        age     : "int",
        created : "timestamp"
    },
    key:["name"]
}

Note that a model class name should contain the word Model in it, otherwise it won't be treated as a model.

Now bind the models directory with express-cassandra

var models = require('express-cassandra');

//Tell express-cassandra to use the models-directory, and
//use bind() to load the models using cassandra configurations.
models.setDirectory( __dirname + '/models').bind(
    {
        clientOptions: {
            contactPoints: ['127.0.0.1'],
            localDataCenter: 'datacenter1',
            protocolOptions: { port: 9042 },
            keyspace: 'mykeyspace',
            queryOptions: {consistency: models.consistencies.one},
            socketOptions: { readTimeout: 60000 },
        },
        ormOptions: {
            defaultReplicationStrategy : {
                class: 'SimpleStrategy',
                replication_factor: 1
            },
            migration: 'safe'
        }
    },
    function(err) {
        if(err) throw err;

        // You'll now have a `person` table in cassandra created against the model
        // schema you've defined earlier and you can now access the model instance
        // in `models.instance.Person` object containing supported orm operations.
    }
);

Alternatively Load & Organize Models Yourself

Alternatively if you don't want to load your models automatically from a specific directory and want to load and sync models yourself, then you can load your schemas like the following:

var ExpressCassandra = require('express-cassandra');
var models = ExpressCassandra.createClient({
    clientOptions: {
        contactPoints: ['127.0.0.1'],
        localDataCenter: 'datacenter1',
        protocolOptions: { port: 9042 },
        keyspace: 'mykeyspace',
        queryOptions: {consistency: ExpressCassandra.consistencies.one},
        socketOptions: { readTimeout: 60000 },
    },
    ormOptions: {
        defaultReplicationStrategy : {
            class: 'SimpleStrategy',
            replication_factor: 1
        },
        migration: 'safe',
    }
});

var MyModel = models.loadSchema('Person', {
    fields:{
        name    : "text",
        surname : "text",
        age     : "int",
        created : "timestamp"
    },
    key:["name"]
});

// MyModel or models.instance.Person can now be used as the model instance
console.log(models.instance.Person === MyModel);

// sync the schema definition with the cassandra database table
// if the schema has not changed, the callback will fire immediately
// otherwise express-cassandra will try to migrate the schema and fire the callback afterwards
MyModel.syncDB(function(err, result) {
    if (err) throw err;
    // result == true if any database schema was updated
    // result == false if no schema change was detected in your models
});

Explanations for the Options Used to Initialize

clientOptions

Any of the clientOptions supported by the cassandra nodejs driver can be used. Possible options are documented in the cassandra driver docs.

ormOptions

If your keyspace doesn't exist it will be created automatically using the defaultReplicationStrategy provided here.

To know more about cassandra replication strategies read the replication docs and for available options for creating keyspace, read the create keyspace docs

Automatic migration is supported. When your model schema changes, the config variable migration defines the migration behaviour.

  • alter will try to alter the corresponding cassandra table to match the new schema. This operation will try to keep the existing data in the table and put null data in the newly created fields. Note that if a field in removed in the changed schema then the column will be dropped from the table and the data associated with the column or field will be lost. Also for primary key or clustering_order changes, the table must be dropped and data will be lost in the process because cassandra won't allow altering primary key. The module will ask for a confirmation from the user in the terminal whether to perform the required alter/drop operations per changed table.

  • drop will always drop and recreate the table and indexes in case of schema change. This will wipe out all data in that table. It will ask for a confirmation from the user in the terminal whether to perform the required drop operations per changed table.

  • safe will send an error message in callback for any kind of model attribute changes. You need to migrate yourself. This is the recommended setting for production. Note that if NODE_ENV==="production" then regardless of the migration setting, safe is always used to protect inadvertent deletion of your data.

Note that some environments might not support tty console, so asking the user for confirmation in the terminal may throw errors. If you face such problems or want to automate the migration process in a dev/staging environment then you can set the property disableTTYConfirmation: true in the ormOptions. This will do the migrations without asking for a confirmation from the user.

The keyspace is created automatically if it does not exist. If you don't want express-cassandra to create the keyspace for you then set createKeyspace: false in ormOptions. It will fire an error if no keyspace with the given name was found.

When express-cassandra syncs your model schema with cassandra, it creates a new table if the table does not exist already. If you don't want express-cassandra to create the new table for you, then set createTable: false in ormOptions. It will fire an error if no table with the given schema table name was found.

Important Note on Migrations Support

Current support for migration is an experimental feature and should be set to safe for production environments. When set to alter or drop the ORM will try to take a conservative approach and will ask the user for confirmation when doing any data destructive operation. But as this feature is new and not yet stable, you might encounter some bugs or glitches here and there. Please report an issue in github if you face any. The team will try their best to fix the problem within short time.

Export/Import Fixture Data

You can dump all table data into json fixture files and reload them back into the tables later on. It's sometimes useful to take a snapshot of current data in your cassandra tables and later populate your database with that data when you're first setting up your app in a new cassandra instance. Also you may use this as a programmable backup system for your app data or whatever use case you can think of.

// exports all table data in current keyspace to the
// directory: 'fixtures' inside current script directory
models.export(__dirname + '/fixtures', function(err){

});

// imports all table data to current keyspace from the
// directory: 'fixtures' inside current script directory
models.import(__dirname + '/fixtures', function(err){

});

// To improve import performance, you may use an optional
// parameter: batchSize to batch the imports in chunks of queries
models.import(__dirname + '/fixtures', { batchSize: 10 }, function(err){

});

Connecting to Cassandra Using Authentication

For connecting to cassandra using authentication, you can use the nodejs-driver authProvider option in the clientOptions object like the following:

clientOptions: {
    contactPoints: ['127.0.0.1'],
    localDataCenter: 'datacenter1',
    protocolOptions: { port: 9042 },
    keyspace: 'mykeyspace',
    queryOptions: {consistency: models.consistencies.one},
    socketOptions: { readTimeout: 60000 },
    authProvider: new models.driver.auth.PlainTextAuthProvider('my_user', 'my_password')
}

If you are using datastax enterprise then please use the auth provider DsePlainTextAuthProvider from cassandra-driver instead.

Let's Insert Some Data into PersonModel


var john = new models.instance.Person({
    name: "John",
    surname: "Doe",
    age: 32,
    created: Date.now()
});
john.save(function(err){
    if(err) {
        console.log(err);
        return;
    }
    console.log('Yuppiie!');
});

Now Let's Find it


models.instance.Person.findOne({name: 'John'}, function(err, john){
    if(err) {
        console.log(err);
        return;
    }
    //Note that returned variable john here is an instance of your model,
    //so you can also do john.delete(), john.save() type operations on the instance.
    console.log('Found ' + john.name + ' to be ' + john.age + ' years old!');
});

Cassandra DB Functions Support

You can use cassandra provided db functions instead of providing a value for a field while inserting, updating or finding an object. For example, we could use the $db_function operator to get the current time for the created field while inserting data for John Doe into the model:


var john = new models.instance.Person({
    name: "John",
    surname: "Doe",
    age: 32,
    created: { $db_function: 'toTimestamp(now())' }
});
john.save(function(err){
    if(err) {
        console.log(err);
        return;
    }
    console.log('Yuppiie!');
});

Built-in Promise Support

Express-cassandra has built-in promise support powered by bluebird. All the orm functions have an Async suffixed pair function that can be used for promise based async operations instead of using callback. For example, if you want to use promises in the above two insert and find operations, you could do the following:

Insert data using promise (note the Async suffix in function name):


var john = new models.instance.Person({
    name: "John",
    surname: "Doe",
    age: 32
});
john.saveAsync()
    .then(function() {
        console.log('Yuppiie!');
    })
    .catch(function(err) {
        console.log(err);
    });

Find data using promise (note the Async suffix in function name):


models.instance.Person.findOneAsync({name: 'John'})
    .then(function(john) {
        console.log('Found ' + john.name + ' to be ' + john.age + ' years old!');
    })
    .catch(function(err) {
        console.log(err);
    });