0.3.11 • Public • Published

Build Status

Documentation currently a work in progress.


A Node.js ORM Framework with

  • adapters (one ships with brianc's node-sql, or write your own over your favourite database and take advantage of the Downstairs framework)
  • associations - belongsTo, hasOne, hasMany
  • validations
  • 'named callbacks' - like middleware at the model layer, helpful to reduce nesting in your model logic.
  • events - define an event lifecycle on a Model.
  • an ectypes testing library allowing the easy creation of test objects - ectypes-downstairs

Downstairs is in production use for three Moneytribe codebases.


Currently in development:

  • eager loading of associations via dynamic left outer joins
  • improved validation objects for easy form integration
  • formal description of when to use the simple JSON query abstraction language or to drop into a dialect specific query


Downstairs has 'Collections' and 'Records'. A Collection is a group of documents. A Record is a single document.

Configure Downstairs with as many database connections and adapters as needed:

var pgConnection = new Downstairs.Connection.PostgreSQL(env.connectionString);
var sqlAdapter = new SQLAdapter();
Downstairs.add(pgConnection, sqlAdapter, "primarydb");

Assign a Collection to a configuration:

var sql = require('sql')
  , Validator = require('validator').Validator;
var userConfig = sql.Table.define({
  name: 'users'
  , quote: true
  , schema: 'public'
  , columns: ['id'
    , 'username'
    , 'created_at'
    , 'updated_at'
    , 'email'
    , 'password'
    , 'role_id'
var validations = {
  passwordPresent: function(cb){
      var validator =  new Validator();
        cb(null, null);
        cb(null, "Password: " + e.message);
var User = Collection.model('User', userConfig, validations, 'primarydb');

Collection level API

Once you have configured a Collection you can manipulate it at a Collection level to produce a record:

  User.create({username: 'fred2', password: 'nottelling', email: ''}, function(err, user) {

Collection level behaviours mandated by every adapter are:

mandatedCalls = ['find', 'findAll', 'update', 'create', 'delete', 'count'];

Some additional query parameters are:

  User.findAll({ like: {name: 'fre%', surname: 'jon%'} });
  # This finds all users with a name that starts with 'fre' and a surname that starts with 'jon'

  User.findAll({ queryParameters: {limit: 6, orderBy: 'name ASC' } } });
  # This orders the query ascending by name and limits the number of results to 6

  User.findAll({ name: [ 'Fred', 'Mary'] });
  # This finds all users named Fred and Mary.

Named Callbacks

Record level API


For migrations use VisionMedia's node-migrate: .

This means you will have to hand craft your SQL to create tables but this is a good thing.

For example, we have a migrations directory and a migrations helper to expedite things. We also export the migration so it can be called upon in tests (when we want to rapidly construct tables after tearing down the database).

# Copyright (c) 2012 MoneyTribe 
migrator = require './helper'
upStatement = "CREATE TABLE users
  id bigserial NOT NULL,
  username character varying(100) UNIQUE NOT NULL,
  created_at timestamp with time zone NOT NULL DEFAULT now(),
  updated_at timestamp with time zone NOT NULL DEFAULT now(),
  is_active boolean DEFAULT true,
  email character varying(512) UNIQUE,
  password character varying(64),
downStatement = "DROP TABLE users;";
exports.up = (next) -> upStatement(err, result) ->
    next() if result
    throw err if err
exports.down = (next)-> downStatement(err, result) ->
    next() if result
    throw err if err
exports.upStatement = upStatement

Getting Started

Install the module with: npm install downstairs

var downstairs = require('downstairs');


  • @nicholasf
  • @kristian-puccio
  • @andy-chilts


Copyright (c) 2012 Moneytribe Pty Ltd. Licensed under the MIT license.




Package Sidebar


npm i downstairs

Weekly Downloads






Last publish


  • nicholasf