0.0.1 • Published 9 years ago

remajo v0.0.1

Weekly downloads
5
License
ISC
Repository
github
Last release
9 years ago

Remajo: REdis MApping -> Js Object

Remajo maps Redis data into javascript objects, supporting multiple levels of hierarchy. It uses a model written in JSON to specify how the data is composed, allowing you to only pull back what you're interested in.

Node Express endpoints are automatically generated for you, exposing your models as a REST API.

Usage

Install via npm:

npm install remajo --save
  1. Create a client in redis.
  2. Pass the client into remajo.builder(), along with your models.
  3. Create a resource from the models you wish to perform actions on
  4. If you wish to expose endpoints, use resource.expose(), passing in an express router var.
//basic express setup
var express = require('express');
var app = express();
var router = express.Router();

...

var redis = require('redis');
var db = redis.createClient();
var remajo = require('remajo');

//initialize your data model
var builder = remajo.builder(client, {
  user: {
    fields: [ 'username', 'avatar' ],
    subs: [
      {type: 'theme'}
    ],
    sets: [
      {type: 'task'}
    ]
  },

  task: {
    fields: [ 'text', 'done' ]
  },

  theme: {
    fields: [ 'name', 'variant' ]
  }
});

//create a resource to allow direct list/read/create/update/delete commands
var user = builder.build('user');

//expose resource as REST endpoints
user.expose(router, '/api/users', true);
app.use('/', router);

Models

Define your object's model in JSON. Fields with an asterisk* are automatically defaulted if no value is supplied.

Type model

KeyDescription
set *(string: pattern) Location of Set identifying all members. Defaults to name:all.
incr *(string) Key where latest id of this object is stored. Remajo will INCR this field to get ids for any new object. Defaults to name:id.
rkey *(string: pattern) Pattern defining the key for this item. "$id" will be replaced with this object's id. Defaults to name:$id.
fields(array) List of fields in this object.
subs(subtype) List of fields
sets(array of subtype) Define hierarchical data by adding subset models.

Subtype

KeyDescription
type(string: pattern) Pattern defining the key for this item. "$id" will be replaced with this object's id.
key *(string: pattern) Location of Set identifying all members
join *(string) Key where latest id of this object is stored. Remajo will INCR this field to get ids for any new object.

Remajo supports string-value object properties, as well as properties that hold an array of another object.

Example user-defined models:

user: {
  fields: [ 'username', 'avatar' ],
  subs: [
    {type: 'theme'}
  ],
  sets: [
    {type: 'task'}
  ]
},

task: {
  fields: [ 'text', 'done' ]
},

theme: {
  fields: [ 'name', 'variant' ]
}

These models automatically receive default fields, resulting in:

user: {
  set: 'user:all',
  incr: 'user:id',
  rkey: 'user:$id',
  fields: [ 'username', 'avatar' ],
  subs: [
    {type: 'theme', key: 'theme', join: '$parent:theme'}
  ],
  sets: [
    {type: 'task', key: 'tasks', join: '$parent:tasks'}
  ]
}

task: {
  set: 'task:all',
  incr: 'task:id',
  rkey: 'task:$id',
  fields: [ 'text', 'done' ]
}

theme: {
  set: 'theme:all',
  incr: 'theme:id',
  rkey: 'theme:$id',
  fields: [ 'name', 'variant' ]
}

This model will translate into an object like the following:

{
  id: 1,
  username: 'Remajo',
  avatar: 'remajo.jpg',
  theme: {
    name: 'meranti',
    variant: 'dark'
  },
  tasks: [
    {id: 100, text: 'find food', done: 'true'},
    {id: 101, text: 'eat food', done: 'false'}
  ]
}

...and in Redis, the corresponding data looks like this:

KeyTypeValue
user:allset 1
user:idstring1
user:1hash{ id: 1, username: 'Remajo', avatar: 'remajo.jpg' }
user:1:themestring 100
theme:idstring100
theme:100hash{ id: 100, name: 'meranti', variant: 'dark' }
user:1:tasksset 1000, 1001
task:idstring1001
task:1000hash{ id: 1000, text: 'find food', done: 'true' }
task:1001hash{ id: 1001, text: 'eat food', done: 'false' }

Remajo uses Redis Strings, Sets and Hashes to store the various pieces of your object's data. Generally, objects properties are stored in a Hash, under the key defined in the rkey pattern.

Patterns

Patterns are very simplistic for now, and only support "$id" to represent a placeholder for the object id, and $parent to represent a placeholder for the parent id.

user:$id "user:1"

Resources

Resources are exposed by calling build() with the name of the type.

//create a resource to allow direct list/read/create/update/delete commands
var user = builder.build('user');
ActionPromise returnsNotes
.list()array(items)retrieve all records listed under model's 'set'
.create(obj)id
.read(id)item
.update(obj)idobj must contain id field, otherwise it will be routed to create().
.del(id)id

All actions return promises:

    user.read(1)
    .then(function(userObj) {
    	//do something with userObj
    });

Endpoints

You can add REST endpoints to your server by calling expose() on you Resource object.

//expose resource as REST endpoints
user.expose(router, '/api/users', true);
app.use('/', router);
ParamTypeDesc
routerExpress.Router
routestringi.e., '/api/users'
exposeSetsboolexpose sets under individual paths

Routes

With a 'user' resource exposed under '/api/users', the following is exposed:

PathMethodAction
/api/usersGETuser.list()
/api/usersPOSTuser.create()
/api/users/:idGETuser.read(:id)
/api/users/:idPUTuser.update(:id)
/api/users/:idDELETEuser.del(:id)

Exposing subsets

The exposeSets option allows you to expose "set" subobject under an individual path. For example, with the following model:

user: {
  fields: [ 'username', 'avatar' ],
  sets: [
    {type: 'task'}
  ]
},

...these additional routes under the following path are exposed:

/api/user/:userId/tasks/:taskId

Using the API behind this route not only updates the Task, but Also updates the links to the parent User object. This make it easy to modify subset items on an individual basis.