A Feathers service adapter for RethinkDB.
JavaScript

README.md

feathers-rethinkdb

Greenkeeper badge

Build Status Dependency Status Download Status

feathers-rethinkdb is a database adapter for RethinkDB, a real-time database.

$ npm install --save rethinkdbdash feathers-rethinkdb

Important: feathers-rethinkdb implements the Feathers Common database adapter API and querying syntax.

This adapter requires a running RethinkDB server.

API

service(options)

Returns a new service instance initialized with the given options. For more information on initializing the driver see the RethinkDBdash documentation.

const r = require('rethinkdbdash')({
  db: 'feathers'
});
const service = require('feathers-rethinkdb');

app.use('/messages', service({
  Model: r,
  db: 'someotherdb', //must be on the same connection as rethinkdbdash
  name: 'messages',
  // Enable pagination
  paginate: {
    default: 2,
    max: 4
  }
}));

Note: By default, watch is set to true which means this adapter monitors the database for changes and automatically sends real-time events. This means that, unlike other databases and services, you will also get events if the database is changed directly.

Options:

  • Model (required) - The rethinkdbdash instance, already initialized with a configuration object. see options here
  • name (required) - The name of the table
  • watch (options, default: true) - Listen to table changefeeds and send according real-time events on the adapter.
  • db (optional, default: none) - Specify and alternate rethink database for the service to use. Must be on the same server/connection used by rethinkdbdash. It will be auto created if you call init() on the service and it does not yet exist.
  • id (optional, default: 'id') - The name of the id field property. Needs to be set as the primary key when creating the table.
  • events (optional) - A list of custom service events sent by this service
  • paginate (optional) - A pagination object containing a default and max page size

adapter.init([options])

Create the database and table if it does not exists. options can be the RethinkDB tableCreate options.

// Initialize the `messages` table with `messageId` as the primary key
app.service('messages').init({
  primaryKey: 'messageId'
}).then(() => {
  // Use service here
});

adapter.createQuery(query)

Returns a RethinkDB query with the common filter criteria (without pagination) applied.

params.rethinkdb

When making a service method call, params can contain an rethinkdb property which allows to pass additional RethinkDB options. See customizing the query for an example.

Example

To run the complete RethinkDB example we need to install

$ npm install @feathersjs/feathers @feathersjs/errors @feathersjs/express @feathersjs/socketio feathers-rethinkdb rethinkdbdash

We also need access to a RethinkDB server. You can install a local server on your local development machine by downloading one of the packages from the RethinkDB website. It might also be helpful to review their docs on starting a RethinkDB server.

Then add the following into app.js:

const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
const socketio = require('@feathersjs/socketio');

const rethink = require('rethinkdbdash');
const service = require('feathers-rethinkdb');

// Connect to a local RethinkDB server.
const r = rethink({
  db: 'feathers'
});

// Create an Express compatible Feathers applicaiton instance.
const app = express(feathers());

// Turn on JSON parser for REST services
app.use(express.json());
// Turn on URL-encoded parser for REST services
app.use(express.urlencoded({extended: true}));
// Enable the REST provider for services.
app.configure(express.rest());
// Enable the socketio provider for services.
app.configure(socketio());
// Register the service
app.use('messages', service({
  Model: r,
  name: 'messages',
  paginate: {
    default: 10,
    max: 50
  }
}));
app.use(express.errorHandler());

// Initialize database and messages table if it does not exists yet
app.service('messages').init().then(() => {
  // Create a message on the server
  app.service('messages').create({
    text: 'Message created on server'
  }).then(message => console.log('Created message', message));

  const port = 3030;
  app.listen(port, function() {
    console.log(`Feathers server listening on port ${port}`);
  });
});

Run the example with node app and go to localhost:3030/messages.

Querying

In addition to the common querying mechanism, this adapter also supports:

$search

Return all matches for a property using the RethinkDB match syntax.

// Find all messages starting with Hello
app.service('messages').find({
  query: {
    text: {
      $search: '^Hello'
    }
  }
});

// Find all messages ending with !
app.service('messages').find({
  query: {
    text: {
      $search: '!$'
    }
  }
});
GET /messages?text[$search]=^Hello
GET /messages?text[$search]=!$

$contains

Matches if the property is an array that contains the given entry.

// Find all messages tagged with `nodejs`
app.service('messages').find({
  query: {
    tags: {
      $contains: 'nodejs'
    }
  }
});
GET /messages?tags[$contains]=nodejs

Customizing the query

In a find call, params.rethinkdb can be passed a RethinkDB query (without pagination) to customize the find results.

Combined with .createQuery(query), which returns a new RethinkDB query with the common filter criteria applied, this can be used to create more complex queries. The best way to customize the query is in a before hook for find. The following example adds a coerceTocondition from RethinkDB coerceTo API to match by any string inside an object.

app.service('mesages').hooks({
  before: {
    find(context) {
      const query = context.service.createQuery(context.params.query);
      
      const searchString = "my search string";
      
      hook.params.rethinkdb = query.filter(function(doc) {
        return doc.coerceTo('string').match('(?i)' + searchString);
      })
    }
  }
});

If you need even more control then you can use service.table (context.service.table in a hook) directly. This way you can create a query from scratch without the the common filter criteria applied. The following example adds a getNearest condition for RethinkDB geospatial queries.

app.service('mesages').hooks({
  before: {
    find(context) {
      const r = this.options.r;

      const point = r.point(-122.422876, 37.777128);  // San Francisco

      // Replace the query with a custom query
      context.params.rethinkdb = context.service.table.getNearest(point, { index: 'location' });
    }
  }
});

Changefeeds

.createQuery(query) can also be used to listen to changefeeds and then send custom events.

Since the service already sends real-time events for all changes the recommended way to listen to changes is with feathers-reactive however.

License

Copyright (c) 2017

Licensed under the MIT license.