{"id":13780262,"url":"https://github.com/feathersjs-ecosystem/feathers-cassandra","last_synced_at":"2025-05-11T13:31:43.501Z","repository":{"id":42231134,"uuid":"146090900","full_name":"feathersjs-ecosystem/feathers-cassandra","owner":"feathersjs-ecosystem","description":"Feathers service adapter for Cassandra DB based on Express-Cassandra ORM and CassanKnex query builder","archived":false,"fork":false,"pushed_at":"2023-07-18T20:19:12.000Z","size":1286,"stargazers_count":8,"open_issues_count":3,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-13T14:13:41.903Z","etag":null,"topics":["cassandra","cassanknex","database","db","express-cassandra","feathers","feathersjs","knex","model","orm"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/feathersjs-ecosystem.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-08-25T11:47:02.000Z","updated_at":"2023-08-29T10:28:09.000Z","dependencies_parsed_at":"2024-01-17T00:55:09.380Z","dependency_job_id":"5d9a72f8-15b3-4642-a23e-3f72931a14f9","html_url":"https://github.com/feathersjs-ecosystem/feathers-cassandra","commit_stats":{"total_commits":245,"total_committers":6,"mean_commits":"40.833333333333336","dds":0.4938775510204082,"last_synced_commit":"e74a1b5a2dbc3eedf22b56b99109bfbdd1e9704c"},"previous_names":[],"tags_count":68,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/feathersjs-ecosystem%2Ffeathers-cassandra","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/feathersjs-ecosystem%2Ffeathers-cassandra/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/feathersjs-ecosystem%2Ffeathers-cassandra/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/feathersjs-ecosystem%2Ffeathers-cassandra/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/feathersjs-ecosystem","download_url":"https://codeload.github.com/feathersjs-ecosystem/feathers-cassandra/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224604408,"owners_count":17339130,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cassandra","cassanknex","database","db","express-cassandra","feathers","feathersjs","knex","model","orm"],"created_at":"2024-08-03T18:01:13.894Z","updated_at":"2024-11-17T15:31:01.660Z","avatar_url":"https://github.com/feathersjs-ecosystem.png","language":"JavaScript","funding_links":[],"categories":["Plugins"],"sub_categories":["Database"],"readme":"# feathers-cassandra\n\n[![Build Status](https://travis-ci.org/feathersjs-ecosystem/feathers-cassandra.svg?branch=master)](https://travis-ci.org/feathersjs-ecosystem/feathers-cassandra)\n[![Coverage Status](https://coveralls.io/repos/github/feathersjs-ecosystem/feathers-cassandra/badge.svg?branch=master)](https://coveralls.io/github/feathersjs-ecosystem/feathers-cassandra?branch=master)\n[![js-semistandard-style](https://img.shields.io/badge/code%20style-semistandard-brightgreen.svg?style=flat-square)](https://github.com/standard/semistandard)\n[![Dependency Status](https://img.shields.io/david/feathersjs-ecosystem/feathers-cassandra.svg)](https://david-dm.org/feathersjs-ecosystem/feathers-cassandra)\n[![npm](https://img.shields.io/npm/v/feathers-cassandra.svg?maxAge=3600)](https://www.npmjs.com/package/feathers-cassandra)\n[![Greenkeeper badge](https://badges.greenkeeper.io/feathersjs-ecosystem/feathers-cassandra.svg)](https://greenkeeper.io/)\n\n[Feathers](https://feathersjs.com/) service adapter for Cassandra DB based on [Express-Cassandra](https://express-cassandra.readthedocs.io) ORM and [CassanKnex](https://github.com/azuqua/cassanknex) query builder\n\n## Installation\n\n```bash\nnpm install --save feathers-cassandra\nnpm install --save express-cassandra\nnpm install --save cassanknex\n```\n\n### [Feathers CLI](https://github.com/feathersjs/cli)\n\nUse `feathers generate service` command to generate a new `Cassandra` service.\n\n## Documentation\n\nPlease refer to the [Feathers database adapter documentation](https://docs.feathersjs.com/api/databases/adapters.html) for more details or directly at:\n\n- [Querying](https://docs.feathersjs.com/api/databases/querying.html) - The common adapter querying mechanism\n- [Extending](https://docs.feathersjs.com/api/databases/common.html#extending-adapters) - How to extend a database adapter\n\nRefer to the official [Express-Cassanndra documention](https://express-cassandra.readthedocs.io).\n\nIt works like the [Knex service](https://github.com/feathersjs/feathers-knex) adapter by using [CassanKnex](https://github.com/azuqua/cassanknex), except it has all\nthe benefits of the Express-Cassandra ORM.\n\n### Service Options\n\n- `model` (**required**) - The Express-Cassandra model definition\n- `id` (*optional*, default: `'id'`) - The name of the id field property. Use array of strings for composite primary keys\n- `events` (*optional*) - A list of [custom service events](https://docs.feathersjs.com/api/events.html#custom-events) sent by this service\n- `paginate` (*optional*) - A [pagination object](https://docs.feathersjs.com/api/databases/common.html#pagination) containing a `default` and `max` page size\n- `multi` (*optional*) - Allow `create` with arrays and `update` and `remove` with `id` `null` to change multiple items. Can be `true` for all methods or an array of allowed methods (e.g. `[ 'remove', 'create' ]`)\n- `whitelist` (*optional*) - A list of additional query operators to allow (e.g. `[ '$token', '$allowFiltering' ]`)\n\n### Default Query Operators\n \nStarting at version 2.0.0 `feathers-cassandra` converts queries securely. If you want to support additional Cassandra operators, the `whitelist` service option can contain an array of additional allowed operators. By default, supported operators are:\n \n```\n$eq\n$ne\n$gte\n$gt\n$lte\n$lt\n$in\n```\n\n### Supported Operators\n\n##### Query Operators\n\n| Operator | Native Operator | Description | Example |\n|:---: | :---: | --- | --- |\n| `$ne` | `!=` | Applicable for IF conditions only | `id: { $ne: 1 }` |\n| `$isnt` | `IS NOT` | Applicable for materialized view filters only | `id: { $isnt: 1 }` |\n| `$gt` | `\u003e` | Greater than | `id: { $ne: 1 }` |\n| `$lt` | `\u003c` | Lower than | `id: { $lt: 1 }` |\n| `$gte` | `\u003e=` | Greater than or equal | `id: { $gte: 1 }` |\n| `$lte` | `\u003c=` | Lower than or equal | `id: { $lte: 1 }` |\n| `$in` | `IN` | Equal to item in list | `id: { $in: [1, 2] }` |\n| `$like` | `LIKE` | Applicable for SASI indexes only | `text: { $like: '%abc%' }` |\n| `$sort` | `ORDER BY` | Sort results | ASC: `$sort: { id: 1 }` DESC: `$sort: { id: -1 }` |\n| `$limit` | `LIMIT` | Sets the maximum number of rows that the query returns | `$limit: 2` |\n| `$select` | `SELECT` | Sets fields to return. you can also select a field with applied Cassandra function: `writetime`, `ttl`, `dateOf`, `unixTimestampOf`, `toDate`, `toTimestamp` \u0026 `toUnixTimestamp` | `$select: ['id', 'name', 'writetime(name)', 'dateOf(name)']` |\n\n##### Cassandra Query Operators\n\n| Operator | Native Operator | Description | Example |\n|:---: | :---: | --- | --- |\n| `$token` | `TOKEN` | Token query on primary keys. can be used for pagination | Single key: `$token: { id: { $gt: 1 } }` Multiple keys: `$token: { $keys: ['id', 'time'], $condition: { $gt: [1, 2] } }`  |\n| `$minTimeuuid` | `minTimeuuid` | Query on `timeuuid` column given a time component. [read more](https://cassandra.apache.org/doc/latest/cql/functions.html#mintimeuuid-and-maxtimeuuid) | `$minTimeuuid: { timeuuid: { $lt: '2013-02-02 10:00+0000' } }` |\n| `$maxTimeuuid` | `maxTimeuuid` | Query on `timeuuid` column given a time component. [read more](https://cassandra.apache.org/doc/latest/cql/functions.html#mintimeuuid-and-maxtimeuuid) | `$maxTimeuuid: { timeuuid: { $gt: '2013-01-01 00:05+0000' } }` |\n| `$contains` | `CONTAINS` | Search in indexed list, set or map | `colors: { $contains: 'blue' }` |\n| `$containsKey` | `CONTAINS KEY` | Search in indexed map | `colors: { $containsKey: 'dark' }` |\n| `$if` | `IF` | Condition that must return TRUE for the update to succeed. Will be used automatically when an update, patch or remove request query by id with additional query conditions | `$if: { name: 'John' }` |\n| `$ifExists` | `IF EXISTS` | Make the UPDATE fail when rows don't match the WHERE conditions | `$ifExists: true` |\n| `$ifNotExists` | `IF NOT EXISTS` | Inserts a new row of data if no rows match the PRIMARY KEY values | `$ifNotExists: true` |\n| `$allowFiltering` | `ALLOW FILTERING` | Provides the capability to query the clustering columns using any condition | `$allowFiltering: true` |\n| `$limitPerPartition` | `PER PARTITION LIMIT` | Sets the maximum number of rows that the query returns from each partition | `$limitPerPartition: 1` |\n| `$ttl` | `USING TTL` | Sets a time in seconds for data in a column to expire. use in create, update \u0026 patch requests | `$ttl: 60` |\n| `$timestamp` | `USING TIMESTAMP` | Sets a timestamp for data in a column to expire. use in create, update \u0026 patch requests | `$timestamp: 1537017312928000` |\n\n##### Special Query Operators\n\n| Operator | Native Operator | Description | Example |\n|:---: | :---: | --- | --- |\n| `$noSelect` |  | Skips SELECT queries in create, update, patch \u0026 remove requests. Response data will be based on the input data | `$noSelect: true` |\n| `$batch` |  | Batch create queries. Response data will be based on the input data | `$batch: true` |\n| `$filters` |  | Sets Model's CassanKnex filters to run on a get or find request | `$filters: ['completed', 'recent']` |\n\n##### Cassandra Data Operators\n\n| Operator | Native Operator | Description | Example |\n|:---: | :---: | --- | --- |\n| `$add` | `+` | Adds to a list, set or map | List/Set: `colors: { $add: ['blue', 'red'] }` Map: `colors: { $add: { dark: 'blue', bright: 'red' } }` |\n| `$remove` | `-` | Removes from a list, set or map | List/Set: `colors: { $remove: ['blue', 'red'] }` Map: `colors: { $remove: ['dark', 'bright'] }` |\n| `$increment` | `+` | Increments a counter | `days: { $increment: 2 }` |\n| `$decrement` | `-` | Decrements a counter | `days: { $decrement: 2 }` |\n\n### Passing Cassandra [queryOptions](https://docs.datastax.com/en/developer/nodejs-driver/4.5/api/type.QueryOptions/)\n\nSet `params.queryOptions` to override options per query, like [setting a different consistency level for a single query](https://docs.datastax.com/en/developer/nodejs-driver/4.5/getting-started/#setting-the-consistency-level).  \n\n### Materialized Views\n\nA materialized view will be automatically queried against when a query contains only that view's keys. \n\n### Model Hooks\n\nWorks like [Express-Cassandra Hook Functions](https://express-cassandra.readthedocs.io/en/stable/management/#hook-functions), but arguments will contain Feathers-Cassandra equivalent objects - data, query, query operators as options \u0026 id. \n\n### Model CassanKnex Filters\n\nFilter functions that call CassanKnex methods on the query builder object before execution.  \n\nFilter functions runs in get \u0026 find requests when specified in the `query.$filters` array.\n\n### Cassandra\n\nSet Cassandra init options as defined in [Cassandra](https://docs.datastax.com/en/developer/nodejs-driver/4.5/api/type.ClientOptions/) \u0026 [Express-Cassandra](https://express-cassandra.readthedocs.io/en/latest/usage/#explanations-for-the-options-used-to-initialize):\n\nconfig/defaults.json\n```json\n{\n  \"cassandra\": {\n    \"clientOptions\": {\n      \"contactPoints\": [\n        \"127.0.0.1\"\n      ],\n      \"protocolOptions\": {\n        \"port\": 9042\n      },\n      \"keyspace\": \"test\",\n      \"queryOptions\": {\n        \"consistency\": 1\n      }\n    },\n    \"ormOptions\": {\n      \"defaultReplicationStrategy\": {\n       \"class\": \"SimpleStrategy\",\n       \"replication_factor\": 1\n      },\n      \"migration\": \"alter\",\n      \"createKeyspace\": true\n    }\n  }\n}\n```  \n\ncassandra.js\n```js\nconst ExpressCassandra = require('express-cassandra')\nconst FeathersCassandra = require('feathers-cassandra')\n\nmodule.exports = function (app) {\n  const connectionInfo = app.get('cassandra')\n  const models = ExpressCassandra.createClient(connectionInfo)\n  const cassandraClient = models.orm.get_system_client()\n\n  app.set('models', models)\n\n  cassandraClient.connect(err =\u003e {\n    if (err) throw err\n\n    const cassanknex = require('cassanknex')({ connection: cassandraClient })\n\n    FeathersCassandra.cassanknex(cassanknex)\n\n    cassanknex.on('ready', err =\u003e {\n      if (err) throw err\n    })\n  })\n}\n```\n\n### Models\n\nDefine [Express-Cassandra Models](https://express-cassandra.readthedocs.io/en/latest/schema/) for your tables:\n\ntodos.model.js\n```js\nmodule.exports = function (app) {\n  const models = app.get('models')\n  const Todo = models.loadSchema('Todo', {\n    table_name: 'todo',\n    fields: {\n      id: 'int',\n      text: {\n        type: 'text',\n        rule: {\n          required: true,\n          validators: [\n            {\n              validator: function (value) { return value !== 'forbidden' },\n              message: '`forbidden` is a reserved word'\n            }\n          ]\n        }\n      },\n      complete: 'boolean',\n      teams: {\n        type: 'map',\n        typeDef: '\u003ctext, text\u003e'\n      },\n      games: {\n        type: 'list',\n        typeDef: '\u003ctext\u003e'\n      },\n      winners: {\n        type: 'set',\n        typeDef: '\u003ctext\u003e'\n      }\n    },\n    key: ['id'],\n    custom_indexes: [\n      {\n        on: 'text',\n        using: 'org.apache.cassandra.index.sasi.SASIIndex',\n        options: {}\n      },\n      {\n        on: 'complete',\n        using: 'org.apache.cassandra.index.sasi.SASIIndex',\n        options: {}\n      }\n    ],\n    options: {\n      // timestamps: true\n      timestamps: {\n        createdAt: 'created_at', // defaults to createdAt\n        updatedAt: 'updated_at' // defaults to updatedAt\n      },\n      // versions: true\n      versions: {\n        key: '_version' // defaults to __v\n      }\n    },\n    filters: {\n      completed: builder =\u003e {\n        builder.where('complete', '=', true)\n      }\n    },\n    before_save: function (instance, options) {\n      instance.complete = false\n      return true\n    },\n    after_save: function (instance, options) {\n      return true\n    },\n    before_update: function (queryObject, updateValues, options, id) {\n      updateValues.complete = true\n      return true\n    },\n    after_update: function (queryObject, updateValues, options, id) {\n      return true\n    },\n    before_delete: function (queryObject, options, id) {\n      return true\n    },\n    after_delete: function (queryObject, options, id) {\n      return true\n    }\n  }, function (err) {\n    if (err) throw err\n  })\n\n  Todo.syncDB(function (err) {\n    if (err) throw err\n  })\n\n  return Todo\n}\n```\n\nWhen defining a service, you must provide the model:\n```js\napp.use('/todos', service({\n  model: Todo\n})\n```\n### Service\n\ntodos.service.js\n```js\nconst createService = require('feathers-cassandra')\nconst createModel = require('./todos.model')\n\nmodule.exports = function (app) {\n  const Model = createModel(app)\n\n  const options = {\n    model: Model,\n    paginate: {\n      default: 2,\n      max: 4\n    },\n    whitelist: ['$allowFiltering', '$filters', '$ttl', '$if']\n  }\n\n  app.use('/todos', createService(options))\n}\n```\n\n### Composite primary keys\n\nComposite primary keys can be passed as the `id` argument using the following methods:\n\n* String with values separated by the `idSeparator` property (order matter, recommended for REST)\n* JSON array (order matter, recommended for internal service calls)\n* JSON object (more readable, recommended for internal service calls)\n\nWhen calling a service method with the `id` argument, all primary keys are required to be passed.\n\n#### Options\n\n* **`idSeparator`** - (optional) separator char to separate Composite primary keys in the `id` argument \n  of get/patch/update/remove external service calls. Defaults to `','`.\n  \n```js\napp.use('/user-todos', service({\n  idSeparator: ','\n})\n\napp.service('/user-todos').get('1,2')\napp.service('/user-todos').get([1, 2])\napp.service('/user-todos').get({ userId: 1, todoId: 2 })\n```  \n\n* **`materializedViews`** - (optional) array of materialized views to use when queries contains the same set of columns that constructs their compound PK.\n\n```js\napp.use('/players', service({\n  materializedViews: [\n    {\n      view: 'top_season_players',\n      keys: [\n        'season',\n        'score'\n      ]\n    }\n  ]\n})\n```  \n\n## Complete Example\n\nHere's a complete example of a Feathers server with a `todos` Feathers-Cassandra service:\n\n```js\nconst feathers = require('@feathersjs/feathers')\nconst express = require('@feathersjs/express')\nconst rest = require('@feathersjs/express/rest')\nconst errorHandler = require('@feathersjs/express/errors')\nconst bodyParser = require('body-parser')\nconst ExpressCassandra = require('express-cassandra')\nconst FeathersCassandra = require('feathers-cassandra')\n\n// Initialize Express-Cassandra\nconst models = ExpressCassandra.createClient({\n  clientOptions: {\n    contactPoints: ['127.0.0.1'],\n    localDataCenter: 'datacenter1',\n    protocolOptions: { port: 9042 },\n    keyspace: 'test',\n    queryOptions: { consistency: ExpressCassandra.consistencies.one }\n  },\n  ormOptions: {\n    defaultReplicationStrategy: {\n      class: 'SimpleStrategy',\n      replication_factor: 1\n    },\n    migration: 'alter',\n    createKeyspace: true\n  }\n})\n\n// Get Cassandra client\nconst cassandraClient = models.orm.get_system_client()\n\n// Connect to Cassandra\ncassandraClient.connect(err =\u003e {\n  if (err) throw err\n\n  // Initialize CassanKnex with the current Cassandra connection\n  const cassanknex = require('cassanknex')({ connection: cassandraClient })\n\n  // Bind CassanKnex\n  FeathersCassandra.cassanknex(cassanknex)\n\n  cassanknex.on('ready', err =\u003e {\n    if (err) throw err\n  })\n})\n\n// Create a feathers instance.\nconst app = express(feathers())\n  // Enable REST services\n  .configure(rest())\n  // Turn on JSON parser for REST services\n  .use(bodyParser.json())\n  // Turn on URL-encoded parser for REST services\n  .use(bodyParser.urlencoded({ extended: true }))\n\napp.set('models', models)\n\n// Create an Express-Cassandra Model\nconst Todo = models.loadSchema('Todo', {\n  table_name: 'todo',\n  fields: {\n    id: 'int',\n    text: {\n      type: 'text',\n      rule: {\n        required: true,\n        validators: [\n          {\n            validator: function (value) { return value !== 'forbidden' },\n            message: '`forbidden` is a reserved word'\n          }\n        ]\n      }\n    },\n    complete: 'boolean',\n    teams: {\n      type: 'map',\n      typeDef: '\u003ctext, text\u003e'\n    },\n    games: {\n      type: 'list',\n      typeDef: '\u003ctext\u003e'\n    },\n    winners: {\n      type: 'set',\n      typeDef: '\u003ctext\u003e'\n    }\n  },\n  key: ['id'],\n  custom_indexes: [\n    {\n      on: 'text',\n      using: 'org.apache.cassandra.index.sasi.SASIIndex',\n      options: {}\n    },\n    {\n      on: 'complete',\n      using: 'org.apache.cassandra.index.sasi.SASIIndex',\n      options: {}\n    }\n  ],\n  options: {\n    timestamps: {\n      createdAt: 'created_at', // defaults to createdAt\n      updatedAt: 'updated_at' // defaults to updatedAt\n    },\n    versions: {\n      key: '_version' // defaults to __v\n    }\n  },\n  filters: {\n    completed: builder =\u003e {\n      builder.where('complete', '=', true) // CassanKnex filter\n    }\n  },\n  before_save: function (instance, options) {\n    instance.complete = false\n    return true\n  },\n  after_save: function (instance, options) {\n    return true\n  },\n  before_update: function (queryObject, updateValues, options, id) {\n    updateValues.complete = true\n    return true\n  },\n  after_update: function (queryObject, updateValues, options, id) {\n    return true\n  },\n  before_delete: function (queryObject, options, id) {\n    return true\n  },\n  after_delete: function (queryObject, options, id) {\n    return true\n  }\n}, function (err) {\n  if (err) throw err\n})\n\nTodo.syncDB(function (err) {\n  if (err) throw err\n})\n\n// Create Cassandra Feathers service with a default page size of 2 items\n// and a maximum size of 4\napp.use('/todos', FeathersCassandra({\n  model: Todo,\n  paginate: {\n    default: 2,\n    max: 4\n  }\n}))\n\n// Handle Errors\napp.use(errorHandler())\n\n// Start the server\nmodule.exports = app.listen(3030)\n\nconsole.log('Feathers Todo FeathersCassandra service running on 127.0.0.1:3030')\n```\n\nRun the example with `node app` and go to [localhost:3030/todos](http://localhost:3030/todos).\n\nYou should see an empty array. That's because you don't have any Todos yet, but you now have full CRUD for your new todos service!\n\n## DB migrations\n\n[Knex Migration CLI](http://knexjs.org/#Migrations) can also be used to manage DB migrations \nand to [seed](http://knexjs.org/#Seeds) a table with mock data:\n\nChange `config.cassandra.ormOptions.migration` to `'safe'`.\n\nCreate `cassanknex.js` file:\n```js\nconst ExpressCassandra = require('express-cassandra');\nconst config = require('config');\nlet cassanknex = null;\n\nconst getCassanknex = async () =\u003e {\n  return new Promise((resolve, reject) =\u003e {\n    if (cassanknex) {\n      resolve(cassanknex);\n\n      return;\n    }\n\n    const connectionInfo = config.cassandra;\n\n    if (connectionInfo.clientOptions.queryOptions.consistency)\n      connectionInfo.clientOptions.queryOptions.consistency = ExpressCassandra.consistencies[connectionInfo.clientOptions.queryOptions.consistency];\n\n    connectionInfo.connection = connectionInfo.clientOptions;\n\n    try {\n      cassanknex = require('cassanknex')(connectionInfo);\n\n      cassanknex.on('ready', function (err) {\n        if (err) {\n          reject(err);\n\n          return;\n        }\n\n        resolve(cassanknex);\n      });\n    } catch (err) {\n      reject(err);\n    }\n  });\n};\n\nmodule.exports = {\n  getCassanknex,\n};\n```\n\nUse it inside a Knex migration file:\n```js\nconst { getCassanknex } = require('../cassanknex');\n\nexports.up = () =\u003e {\n  return new Promise(async (resolve, reject) =\u003e {\n    const cassanknex = await getCassanknex();\n\n    cassanknex('example').createColumnFamilyIfNotExists('table')\n      .uuid('id')\n      .text('data')\n      .primary('id')\n      .exec((err, result) =\u003e {\n        if (err) {\n          reject(err);\n\n          return;\n        }\n\n        resolve();\n      });\n  });\n};\n\nexports.down = () =\u003e {\n  return new Promise(async (resolve, reject) =\u003e {\n    const cassanknex = await getCassanknex();\n\n    cassanknex('example').dropColumnFamilyIfExists('table')\n      .exec((err, result) =\u003e {\n        if (err) {\n          reject(err);\n\n          return;\n        }\n\n        resolve();\n      });\n  });\n};\n```\n\n## Error handling\n\nAs of version 3.4.0, `feathers-cassandra` only throws [Feathers Errors](https://docs.feathersjs.com/api/errors.html) with the message.  \nOn the server, the original error can be retrieved through a secure symbol via  `error[require('feathers-cassandra').ERROR]`.\n\n```js\nconst { ERROR } = require('feathers-cassandra');\n\ntry {\n  await cassandraService.doSomething();\n} catch (error) {\n  // error is a FeathersError with just the message\n  // Safely retrieve the original error\n  const originalError = error[ERROR];\n}\n```\n\n## Migrating to `feathers-cassandra` v2\n \n`feathers-cassandra` 2.0.0 comes with important security and usability updates.\n\n\u003e __Important:__ For general migration information to the new database adapter functionality see [docs.feathersjs.com/guides/migrating.html#database-adapters](https://docs.feathersjs.com/guides/migrating.html#database-adapters).\n\nThe following breaking changes have been introduced:\n\n- All methods allow additional query parameters\n- Multiple updates are disabled by default (see the `multi` option)\n- Cassandra related operators are disabled by default (see the `whitelist` option)\n\n## License\n\nCopyright © 2020\n\nLicensed under the [MIT license](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffeathersjs-ecosystem%2Ffeathers-cassandra","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffeathersjs-ecosystem%2Ffeathers-cassandra","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffeathersjs-ecosystem%2Ffeathers-cassandra/lists"}