Find file
Fetching contributors…
Cannot retrieve contributors at this time
173 lines (135 sloc) 4.18 KB


render associative data from level-assoc with hyperkey for live server+client rendering with progressive enhancement


In the browser, just load hyperkey render modules and specify nested rendering sub-components. In this case, each hackerspace has a nested collection of hackers:

var shoe = require('shoe');
var sock = shoe('/sock');
var rassoc = require('render-assoc');

var render = require('./render/hackerspace.js')();

render.pipe(sock).pipe(rassoc(render, {
    hacker: require('./render/hacker.js')


The associations on the server server-side are:

var level = require('level-test')();
var sub = require('level-sublevel');
var db = sub(level('hyperkey-example.db', { valueEncoding: 'json' }));
var assoc = require('level-assoc')(db);

    .hasMany('hackers', [ 'type', 'hacker' ])
    .hasMany('tools', [ 'type', 'tool' ])
    .hasMany('projects', [ 'type', 'project' ])
    .hasMany('usage', [ 'type', 'usage' ])
    .hasMany('usage', [ 'type', 'usage' ])

module.exports = assoc;

we can write a simple server that uses assoc.track() to plumb up reactive live updates over websockets:

var http = require('http');
var ecstatic = require('ecstatic')(__dirname + '/static');
var trumpet = require('trumpet');
var fs = require('fs');

var assoc = require('./data.js');
var render = require('./render/hackerspace.js');

var server = http.createServer(function (req, res) {
    if (req.url === '/') {
        var tr = trumpet();
        var q = assoc.list('hackerspace');

        var elem ='#hackerspaces');
        elem.setAttribute('data-start', q.startKey);
        elem.setAttribute('data-end', q.endKey);

    else ecstatic(req, res);

var shoe = require('shoe');
var sock = shoe(function (stream) {
sock.install(server, '/sock');

function readStream (file) {
    return fs.createReadStream(__dirname + '/static/' + file);

And the shared hyperkey rendering code and html looks like:

<div class="hacker">
  <i class="name"></i>
var hyperkey = require('hyperkey');
var fs = require('fs');
var html = fs.readFileSync(__dirname + '/hacker.html');

module.exports = function () {
    return hyperkey(html, function (row) {
        return { '.name': };
<div class="hackerspace">
  <h1 class="name"></h1>
  <div class="hackers"></div>
var hyperkey = require('hyperkey');
var fs = require('fs');
var html = fs.readFileSync(__dirname + '/hackerspace.html');
var hacker = require('./hacker.js');

module.exports = function () {
    return hyperkey(html, function (row) {
        return {
            '.hackers': (function (stream) {
                return {
                    'data-start': stream.startKey,
                    'data-end': stream.endKey,
                    _html: stream.pipe(hacker())

Compile the browser code with browserify and brfs and then your nested collections will automatically update as the data changes on the server while preserving server-side rendering!


var rassoc = require('render-assoc');

rassoc(render, subRenderers)

Create a writable stream for assoc.track() data to be written to across a websocket from a hyperkey render function and an object of subRenders mapping assocation types to sub-collection render functions.

In a later release this will be more generalized to account for multiple render targets through the same stream.


With npm do:

npm install render-assoc