Skip to content

Plugin: Model Registry

Martin Muzatko edited this page Mar 27, 2018 · 9 revisions

Introduction

When defining relationships between your models and collections, you're probably going to run into issues with circular dependencies. Node returns unfinished copies of modules when you define circular dependencies. For example:

// file: a.js
var b = require('./b'); // => {}
module.exports = 'foo';

// file: b.js
var a = require('./a'); // => {}
module.exports = function() {
  return 'bar';
};

//file: c.js
var b = require('./b');
b(); // => TypeError, a is not a function

Because Node creates empty copies of modules to avoid infinite loops with circular dependencies, even c.js never gets the module.exports object of b.js. Circular dependencies are almost guaranteed if you're defining relationships between your model. To help get around this, Bookshelf lets you register your models and collections in a central location so you can call them without dependency issues.

Getting Started

Load the plugin on your Bookshelf instance using Bookshelf.plugin('registry').

Methods

bookshelf.model(name, [Model], [staticProps]) => Model

Registers a model. Omit Model to return a previously registered model with the provided name.


bookshelf.collection(name, [Collection], [staticProps]) => Collection

Same semantics as Bookshelf.model.


Relations

The plugin monkey patches Bookshelf's relation functions (hasOne, hasMany, belongsTo, belongsToMany, through, morphOne, morphMany, morphTo). Anywhere you'd normally require a model, you can refer to it using its name supplied to Bookshelf.model.

For example:

// file: customer.js
require('./order');
module.exports = bookshelf.model('Customer', {
  tableName: 'customers',
  orders: function() {
    return this.hasMany('Order');
  }
});
// file: order.js
require('./customer');
module.exports = bookshelf.model('Order', {
  tableName: 'orders',
  customer: function() {
    return this.belongsTo('Customer');
  }
});

or alternatively, with a defined model:

// file: customer.js
require('./order');
var Customer = Bookshelf.Model.extend({
  tableName: 'customers',
  orders: function() {
    return this.hasMany('Order');
  }
});

module.exports = Bookshelf.model('Customer', Customer);
// file: order.js
require('./customer');
var Order = Bookshelf.Model.extend({
  tableName: 'orders',
  customer: function() {
    return this.belongsTo('Customer');
  }
});

module.exports = Bookshelf.model('Order', Order);

You still have to require the model somewhere to make sure Bookshelf.model is called. Here we're requiring it at the top of each file to make sure it's loaded into the registry, but we don't actually need to assign it to a variable. You don't have to worry that the module won't actually be available when required because of how Node handles circular references. By the time you call new Order({id: 1}).customer(), the module will be ready and your relationships will work as expected.

Finally, note that it's possible to both use this plugin for some models/collections while still using the classical instantiation way for the other models/collections.This plugin is not an all-or-nothing strategy.