dmfay / massive-js

A data mapper for Node.js and PostgreSQL.
2.49k stars 158 forks source link

feat: Lazy async interface #558

Closed Zolmeister closed 6 years ago

Zolmeister commented 6 years ago

I've been using the following wrapper that others may find useful. Basically it allows immediate usage of the massive promise as if it were resolved, and only when a function is called does it actually connect and resolve the promise (the first time)

// db.js
var _ = require('lodash');
var massive = require('massive');
var config = require('../config');

lazy = _.memoize(function() {
  return massive({
    user: config.POSTGRES.USER,
    host: config.POSTGRES.HOST,
    database: config.POSTGRES.DB
  });
});

proxy = function(target, path) {
  var fn = function() {
    var args = arguments;
    return lazy().then(function(db) {
      var targetFn = _.get(db, path);
      if (targetFn == null) {
        throw new Error("Unknown method: " + path);
      }
      var pre = path.slice(0, path.lastIndexOf('.'));
      return targetFn.apply(_.get(db, pre) || db, args);
    });
  };
  return new Proxy(fn, {
    get: function(fn, name) {
      return proxy(target, path + '.' + name);
    }
  });
};

module.exports = new Proxy({}, {
  get: function(target, name) {
    return proxy(target, name);
  }
});
// proposed usage
db = require('massive')({host:...})
db.user.find(1).then()...
db.user.insert({}).then()...
dmfay commented 6 years ago

Appreciated, but I have to close it since it isn't really actionable.

Zolmeister commented 6 years ago

To clarify, I'm proposing the default massive promise behave like this proxy object

dmfay commented 6 years ago

Sell me on it, then. What's the advantage of lazy init over just taking the introspection hit at startup? What makes it worth the extra complexity?

Zolmeister commented 6 years ago

It's side-effect free Otherwise you have to explicitly mutate a global instance or pay the resolution cost on each usage

// this everywhere
db.then (db) -> ...
// or
// pg.js
module.exports = {db: null}
// setup.js
massive().then(db => pg.db = db) // side-effect
dmfay commented 6 years ago

ah. With ES2017 you can db = await massive(...); which more or less obviates the issue for Node 7+. That leaves 6, and it's a lot less compelling to add and support new features that only matter for the oldest version Massive supports, especially when co exists.

robertrossmann commented 6 years ago

And you can also explicitly create and connect new instance without the need to await it everywhere:

const Database = require('massive').Database
const db = new Database(/* args here */)
await db.reload() // I think...