npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

remajo

v0.0.1

Published

REdis -> MApped -> Javascript Objects

Readme

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

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.