remajo v0.0.1
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
- Create a client in redis.
- Pass the client into remajo.builder(), along with your models.
- Create a resource from the models you wish to perform actions on
- 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
Key | Description |
---|---|
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
Key | Description |
---|---|
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:
Key | Type | Value |
---|---|---|
user:all | set | 1 |
user:id | string | 1 |
user:1 | hash | { id: 1, username: 'Remajo', avatar: 'remajo.jpg' } |
user:1:theme | string | 100 |
theme:id | string | 100 |
theme:100 | hash | { id: 100, name: 'meranti', variant: 'dark' } |
user:1:tasks | set | 1000, 1001 |
task:id | string | 1001 |
task:1000 | hash | { id: 1000, text: 'find food', done: 'true' } |
task:1001 | hash | { 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');
Action | Promise returns | Notes |
---|---|---|
.list() | array(items) | retrieve all records listed under model's 'set' |
.create(obj) | id | |
.read(id) | item | |
.update(obj) | id | obj 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);
Param | Type | Desc |
---|---|---|
router | Express.Router | |
route | string | i.e., '/api/users' |
exposeSets | bool | expose sets under individual paths |
Routes
With a 'user' resource exposed under '/api/users', the following is exposed:
Path | Method | Action |
---|---|---|
/api/users | GET | user.list() |
/api/users | POST | user.create() |
/api/users/:id | GET | user.read(:id) |
/api/users/:id | PUT | user.update(:id) |
/api/users/:id | DELETE | user.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.
9 years ago